Question

So I just found out that it's legal to signal a condition variable if you're not holding the lock in c++11. That seems to open the door to some nasty race condition:

std::mutex m_mutex;
std::condition_variable m_cv;

T1: 
  std::unique_lock<std::mutex> lock(m_mutex);
  m_cv.wait(lock, []{ return !is_empty(); });

T2:
  generate_data();
  m_cv.notify();

Is it guaranteed that T1 will never end up in a situation where we check is_empty() first (it returning true), then getting preempted by T2 which creates some data and signals the condition variable before we can actually wait on it?

If this is guaranteed to work (I'd guess so, otherwise it would seem like an intentionally bad API design), how is this actually implemented for say linux and stdlibc++? Seems we'd need another lock to avoid this situation.

Was it helpful?

Solution

Checking the predicate and waiting are not performed atomically in std::condition_variable::wait (unlocking the lock and sleeping are performed atomically). If it is possible for another thread to change the value of the predicate while this thread holds the mutex, then it is possible for notifications to occur between the predicate check and going to sleep, and effectively be lost.

In your example, if generate_data() in T2 can alter the result of is_empty() without holding m_mutex, it's possible for a notification to happen between T1 checking is_empty() and sleeping on m_cv. Holding the mutex at any time between the change to the predicate and the notification is sufficient to guarantee the atomicity of the predicate check and wait call in the other thread. That could look like:

{
  std::lock_guard<std::mutex> lk(m_mutex);
  generate_data();
}
m_cv.notify();

or even

generate_data();
std::lock_guard<std::mutex>(m_mutex); // Lock the mutex and drop it immediately
m_cv.notify();

OTHER TIPS

It is not guaranteed - if you don't want to miss the signal then you must lock the mutex prior to notifying. Some applications may be agnostic about missing signals.

From man pthread_signal:

The pthread_cond_signal() or pthread_cond_broadcast() functions may be called by a thread whether or not it currently owns the mutex that threads calling pthread_cond_wait() or pthread_cond_timedwait() have associated with the condition variable during their waits; however, if predictable scheduling behaviour is required, then that mutex is locked by the thread calling pthread_cond_signal() or pthread_cond_broadcast().

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top