Consider the following scenario:
std::atomic<bool> condition{false};
std::mutex mutex;
std::condition_variable cv;
void waiter() {
std::unique_lock lk(m);
cv.wait(lk, []{ return condition; });
}
void setter() {
condition = true;
cv.notify_one();
}
The above code is race free when waiter
and setter
functions run on different threads. However, it is not correct. The Condition Variable can wait indefinitely.
See std condition_variable wait - cppreference.com;
Consider the case:
- Initialisation happens, and
waiter()
&setter()
are executed on separate threads - waiter() thread: Obtains a unique lock on the mutex.
- waiter() thread:
cv.wait()
checks thatcondition
is false but context switches before it waits oncv
. Note that it is still holding the lock. - setter() thread: Sets
condition
to true and notifiescv
. No thread is woken up because no thread is sleeping on it. - waiter() thread: Starts to wait on
cv
, then releases the lock. This will end up waiting indefinitely or until a spurious wakeup happens.
Observe that if condition
had been set after locking, the set could not have happened in between the functioning of cv.wait()
.
Info
When waiting on a variable through a condition variable, the variable has to be set only after acquiring the associated lock.