Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::timed_mutex::try_lock* fail spuriously

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 return false even if the mutex was not locked by any other thread at some point during timeout_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?

like image 577
Lingxi Avatar asked Nov 25 '15 03:11

Lingxi


People also ask

What is a Try_lock?

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.

How do I use mutex lock in CPP?

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.


2 Answers

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:

  1. 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.
  2. 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.
  3. 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.
like image 187
Anders Avatar answered Oct 23 '22 10:10

Anders


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 return false 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
like image 33
Victor Dyachenko Avatar answered Oct 23 '22 09:10

Victor Dyachenko