By try_lock*
, I take to mean try_lock()
, try_lock_for()
, and try_lock_until()
. According to cppreference, all three methods may just fail spuriously. Following is quoted from the description for try_lock_for()
As with
try_lock()
, this function is allowed to fail spuriously and returnfalse
even if the mutex was not locked by any other thread at some point duringtimeout_duration
.
I know that spurious wakeup may happen with std::condition_variable
and the rationale behind it. But, what is the case with a mutex?
mutex::try_lockTries to lock the mutex. Returns immediately. On successful lock acquisition returns true, otherwise returns false. This function is allowed to fail spuriously and return false even if the mutex is not currently locked by any other thread.
std::mutex::lock Locks the mutex. If another thread has already locked the mutex, a call to lock will block execution until the lock is acquired. If lock is called by a thread that already owns the mutex , the behavior is undefined: for example, the program may deadlock.
According to: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3209.htm
On the other hand, there are strong reasons to require that programs be written to tolerate spurious try_lock() failures:
- As pointed out in Boehm, Adve, "Foundations of the C++ Concurrency Memory Model", PLDI 08, enforcing sequential consistency for data-race-free programs without spurious try_lock() failures requires significantly stronger memory ordering for lock() operations on try_lock()-compatible mutex types. On some architectures that significantly increases the cost of uncontended mutex acquisitions. This cost appears to greatly outweigh any benefit from prohibiting spurious try_lock() failures.
- It allows a user-written try_lock() to fail if, for example, the implementation fails to acquire a low-level lock used to protect the mutex data structure. Or it allows such an operation to be written directly in terms of compare_exchange_weak.
- It ensures that client code remains correct when, for example, a debugging thread is introduced that occasionally acquires locks in order to be able to read consistent values from a data structure being checked or examined. Any code that obtains information from try_lock() failure would break with the introduction of another thread that purely locks and reads the data structure.
From C++14 chapter "30.4.1.2 Mutex types"
paragraph 16:
An implementation may fail to obtain the lock even if it is not held by any other thread. [Note: This spurious failure is normally uncommon, but allows interesting implementations based on a simple compare and exchange (Clause 29). —end note] An implementation should ensure that
try_lock()
does not consistently returnfalse
in the absence of contending mutex acquisitions.
and paragraph 19:
little would be known about the state after a failure, even in the absence of spurious failures
And in answer to
I know that spurious wakeup may happen with std::condition_variable and the rationale behind it. But, what is the case with a mutex?
std::timed_mutex
sometimes is implemented using std::condition_varible
when there is no direct support in the OS. As in GNU libstdc++:
#if _GTHREAD_USE_MUTEX_TIMEDLOCK
...
#else // !_GTHREAD_USE_MUTEX_TIMEDLOCK
class timed_mutex
{
mutex _M_mut;
condition_variable _M_cv;
bool _M_locked = false;
public:
template<typename _Rep, typename _Period>
bool
try_lock_for(const chrono::duration<_Rep, _Period>& __rtime)
{
unique_lock<mutex> __lk(_M_mut);
if (!_M_cv.wait_for(__lk, __rtime, [&]{ return !_M_locked; }))
return false;
_M_locked = true;
return true;
}
template<typename _Clock, typename _Duration>
bool
try_lock_until(const chrono::time_point<_Clock, _Duration>& __atime)
{
unique_lock<mutex> __lk(_M_mut);
if (!_M_cv.wait_until(__lk, __atime, [&]{ return !_M_locked; }))
return false;
_M_locked = true;
return true;
}
};
#endif
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With