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 thatconditionis false but context switches before it waits oncv. Note that it is still holding the lock. - setter() thread: Sets
conditionto 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.