Condition variable waiting on multiple mutexes

5.7k views Asked by At

I have a std::condition_variable_any that waits on a custom lock which is a composition of two mutexes (one std::mutex and one shared-locked std::shared_mutex). Its unlock() operation simply unlocks both mutexes sequentially.

For example (pseudocode):

mutex mutex1;
shared_mutex mutex2;
condition_variable_any cv;
// acquiring the locks
DualLock lock(unique_lock(mutex1), shared_lock(mutex2));
// waiting
cv.wait(lock);

cv.wait() should atomically unlock both mutex1 and mutex2, and put the thread to sleep until cv gets notified.

It it still guaranteed that the thread is sleeping and listening to the condition variable notification, once any of mutex1 or mutex2 is unlocked?

Or is it possible that one mutex gets unlocked, and a second thread locks it, sends the notification, but this first thread was not yet sleeping and listening. So the notification never arrived and no wakeup occurs.

1

There are 1 answers

2
Richard Hodges On BEST ANSWER

If DualLock meets the requirements of BasicLockable, then the code will perform as hoped. If not, it won't.

BasicLockable reference is here

So the notification never arrived and no wakeup occurs.

This can never happen when condition variables are used properly. The wakeup of a condition variable cannot be interpreted as a signal of an event, since the documentation of condition_variable explains that there can be spurious wakeups.

At best, the notification is an indication that now may be a good time to test the condition you're waiting for (and you may do so in the secure knowledge that the test is protected by the mutex).

The state of the condition and the state of the blocked-ness of the current thread are two separate concerns.

Here's a better example (assumes that DualLock models BasicLockable correctly):

bool condition_met = false;

mutex mutex1;
shared_mutex mutex2;
condition_variable_any cv;
// acquiring the locks
DualLock lock(unique_lock(mutex1), shared_lock(mutex2));
while(!condition_met)
{
    // waiting
    cv.wait(lock);
}
// whatever happens, you have the lock here

// ...work

lock.unlock();

Your notifier would notify this way:

DualLock lock(unique_lock(mutex1), shared_lock(mutex2));
condition_met = true;
lock.unlock();     // note the order: unlock mutex first...
cv.notify_all();   // ...then notify the condition variable