c++language-lawyerc++17mutexscoped-lock

std::scoped_lock behaviour with a single mutex


Context:

I know that std::lock_guard became kind of deprecated since the arrival of with std::scoped_lock.

I also know that std::scoped_lock is preferred since it can handle several mutexes and uses a deadlock avoidance algorithm the same way as std::lock does.

I'm interested here in the case we have only one single mutex and thus we don't need to care about deadlock avoidance.

I have read from this answer that:

You can consider std::lock_guard deprecated. The single argument case of std::scoped_lock can be implemented as a specialization and such you don't have to fear about possible performance issues.

Question:

I'm wondering how much this sentence is true.

I mean, is it guaranteed (by the standard) that with a single mutex, std::scoped_lock will be specialized so that it will get rid of any unnecessary overhead due to deadlock avoidance handling ?


My thoughts:

After some investigation on the question, I found from cppreference the following sentence:

If several mutexes are given, deadlock avoidance algorithm is used as if by std::lock.

Which could let us deduce that such a thing would not happen otherwise (i.e. if only one mutex is given).
But once again, it is just an assumption.

From this c++ draft I don't see any explicit mention about such a specialization.
The only sentence I got is:

When sizeof...(MutexTypes) is 1, the supplied Mutex type shall meet the Cpp17BasicLockable requirements. Otherwise, each of the mutex types shall meet the Cpp17Lockable requirements.

(emphasis mine)

I know that the BasicLockable requirements mandate the existence of lock() and unlock() functions which meet the conditions such as defined here.
On the other hand, the Lockable requirements assume the BasicLockable requirements with the addition of a try_lock() function which meet the conditions such as defined there.

I know that the try_lock() function is required in order to run the deadlock avoidance algorithm used by std::lock.

From what's stated in the above draft extract, the try_lock() function is thus not required if we give only one mutex to std::scoped_lock.
Is this sufficient to deduce/consider that the above specialization is always defined (and presumably behaves as std::lock_guard would do).
I would say yes but as I never saw any explicit mention about it, I wonder if I'm right or if I missed something ?


EDIT:

I just noticed that I missed the most important part here which states:

Effects: Initializes pm with tie(m...). Then if sizeof...(MutexTypes) is 0, no effects. Otherwise if sizeof...(MutexTypes) is 1, then m.lock(). Otherwise, lock(m...).

(emphasis mine)

Which answers my interrogations, std::lock is called only when there is more than one given mutex. I should have seen it before asking the question...


Solution

  • If you read the specification of lock_guard (which is right above scoped_lock) it should be clear.

    [thread.lock.guard]-3

    Initializes pm with m. Calls m.lock()

    [thread.lock.scoped]-3

    Initializes pm with tie(m...). [...] Otherwise if sizeof...(MutexTypes) is 1, then m.lock(). [...]

    It doesn't explicitly mention to use lock_guard but it is required to have the same behavior.