Waiting for a state predicate
A consumer waiting on an empty queue needs the predicate “queue is nonempty,” not a particular function call. It examines the queue while holding the mutex; a producer changes state under the same synchronization protocol and notifies possible waiters.
C++ wait atomically unlocks the associated mutex and blocks, closing the gap between checking state and waiting. It reacquires the mutex before returning so other threads can change the protected state during the wait.
Waking does not reserve the condition
C++ and Rust allow spurious wakeups. Even a genuine notification can be followed by another consumer taking the item first. Waiters therefore recheck predicates in a loop or use predicate-taking interfaces rather than treating a return as proof of readiness.
notify_one wakes an existing waiter and notify_all addresses all current waiters; neither reserves a queue item or stores future permits when nobody is waiting. Persistent facts belong in protected state, while resource counts can use a different abstraction such as a semaphore.
Timeouts still require state and deadline checks
A timeout offers another reason to stop waiting without proving that a business condition never held or that the thread runs at an exact instant. After return and mutex reacquisition, the predicate and interface result guide the next step.
Restarting a full relative timeout on each loop iteration can extend total waiting after wakeups. Rust documents this issue and supplies predicate-aware timed waiting. A common deadline or a suitable combined interface keeps retries within the intended overall budget.