Implementing condition_variable timed_wait correctly
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Timed waits on condition variables are easy to get subtly wrong because wakeups are not guarantees. A thread can wake up spuriously, another thread can consume the condition first, and relative timeouts can accidentally stretch longer than intended if you restart them in a loop. The correct pattern is to protect shared state with a mutex and wait against a predicate tied to an absolute deadline.
Always Wait for a Predicate
The condition variable itself does not store the condition. Your shared state does. The thread should therefore wait until a predicate over that state becomes true.
The predicate matters because condition variables can wake up without the condition actually being satisfied.
Timed Wait with an Absolute Deadline
For timeout behavior, prefer an absolute deadline with wait_until rather than repeatedly using a fresh relative duration in a loop.
This returns true if ready becomes true before the deadline and false if the wait times out.
Using steady_clock is important because it is monotonic and not affected by wall-clock adjustments.
Why Relative Waits Can Go Wrong
A common mistake is:
This looks reasonable, but it is wrong if you mean "wait at most one second total". A spurious wakeup after 900 milliseconds restarts the full one-second timer, so the total wait can silently exceed the intended bound.
By computing the deadline once and waiting until that fixed point, you avoid timeout inflation.
Signaling Thread Pattern
The waiting side is only half of the story. The notifying side must update the shared state while holding the same mutex, then notify waiting threads.
The lock ensures the state change and the waiting predicate are synchronized correctly. The notification tells waiters to re-check the predicate.
Full Example with Shared Queue
Here is a small producer-consumer style example using a timed wait:
This is the usual shape of correct timed waiting in real code: lock, predicate, absolute deadline, then consume the shared state.
Common Pitfalls
The biggest mistake is treating notify_one or notify_all as proof that the condition is now true. Notifications only mean "check again". The predicate still decides whether the waiting thread should proceed.
Another issue is waiting without a predicate. That makes the code vulnerable to spurious wakeups and races where another thread changes the state between notification and reacquisition of the mutex.
Using a relative timeout repeatedly in a loop is another classic bug. It often turns a one-second intended limit into an arbitrarily long wait under repeated wakeups.
Finally, use steady_clock for deadlines. System clock jumps can make wall-clock-based timeouts behave unpredictably.
Summary
- Condition variables wait for shared state, not for notifications by themselves.
- Always pair a timed wait with a predicate protected by the same mutex.
- Use
wait_untilwith a single absolute deadline when you need a true overall timeout. - Handle signaling by changing the shared state first and notifying after that.
- Prefer
steady_clockso timeout behavior is stable even if the system clock changes.

