278. Condvar::wait_while — The Wait Loop You Keep Writing (and Getting Backwards)
A Condvar can wake up for no reason at all — so every correct wait is a loop. wait_while writes that loop for you.
The textbook pattern: a worker flips a flag under a Mutex, and a waiting thread sleeps until it’s flipped. The naive version is broken:
| |
wait is allowed to return even though nobody called notify_one — a spurious wakeup. The OS primitives underneath make no stronger promise, so neither does Rust. Correct code re-checks in a loop:
| |
That works, but it’s boilerplate with two sharp edges: forget the loop and you have a heisenbug that only fires under load; shuffle the guard rebinding and it won’t compile. wait_while folds the whole dance into one call:
| |
The predicate answers “should I keep waiting?” — it returns true to sleep, false to wake. That inversion is the one thing to internalize: you pass |ready| !*ready, not |ready| *ready. Read it as “wait while not ready.”
Full picture with a notifying thread:
| |
The predicate runs with the lock held, so it can inspect any state the mutex guards — a queue’s length, a counter, a shutdown flag — not just booleans. And because wait_while re-checks after every wakeup, spurious or real, the guard it returns is guaranteed to satisfy your condition.
There’s a deadline-aware sibling, wait_timeout_while, which additionally hands back a WaitTimeoutResult so you can tell “condition met” from “gave up.”
If all you’re guarding is “has this one value been initialized,” OnceLock::wait is the simpler tool. Condvar earns its keep when the condition is richer than init-once — and wait_while is how you use it without rediscovering spurious wakeups in production.
Stable since Rust 1.42.