Coordination primitives — every question, written out
Condition variables and the while loop that is not optional, semaphores that bound a buffer, barriers that make a phase boundary real.
Why must a condition-variable wait sit inside a `while` rather than an `if`?
Invariant identification
A woken thread must requeue for the mutex, and the condition can become false again before it gets in.
Being woken is not a promise that the condition holds — it is a suggestion to go and look. Between the wake and the reacquisition another thread can take exactly the item the wakeup was about.
See every interleaving — `if`: 6 of 30 take from an empty buffer. `while`: 0 of 30.
Two consumers wait inside an `if`, one item is produced, and the producer broadcasts. What happens?
Code diagnosis
Both wake, the first takes the item, and the second takes from an empty buffer.
A broadcast wakes all waiters; only one can be satisfied. With `if`, the second proceeds on a condition that was true when it was told and false by the time it acted.
See every interleaving — The variant one click away.
A producer signals before any consumer has called `wait`. What happens to the signal?
Edge case reasoning
It is discarded — condition variables have no memory of past signals.
This is the lost wakeup, and it is why the predicate rather than the notification is the source of truth. A consumer that waits without first checking the state can wait forever for something that already happened.
See every interleaving — One of two interleavings deadlocks.
When is `signal` safe to use instead of `broadcast`?
Trade-off & selection
When every waiter is waiting for the same condition and any one can consume the event.
If waiters are waiting for different predicates on the same variable, `signal` may wake one that cannot proceed while the one that could stays asleep. Use `broadcast`, or one condition variable per predicate.
What does `wait` do to the mutex?
Invariant identification
Releases it atomically as part of entering the wait, and reacquires it before returning.
The atomicity is what prevents a signal from slipping between the predicate check and the wait. Any gap there and the signal could arrive while the thread is not yet waiting, and be lost.
How do two semaphores implement a bounded buffer?
Explain it plainly
Two counting semaphores facing opposite ways: `slots` starting at capacity, `items` starting at zero. A producer acquires a slot, inserts, releases an item; a consumer does the reverse. Nobody checks a size or compares against a limit — the permits are the limit, and a producer that cannot get a slot simply cannot proceed.
One counts empty slots and starts at the capacity; the other counts items and starts at zero. Producing takes a slot and releases an item; consuming takes an item and releases a slot, so the bound maintains itself.
See every interleaving — A producer blocked on the slot semaphore is backpressure.
In a bounded buffer, does it matter whether you take the mutex or the slot semaphore first?
Edge case reasoning
Yes — mutex first deadlocks, blocking on a permit while holding the lock.
Semaphore first, then mutex. Reversing it means a full buffer leaves the producer blocked on a permit while holding the lock the consumer must take to free one — hold and wait, with a cycle.
What does an unbounded queue between a fast producer and a slow consumer actually do?
Trade-off & selection
Converts an error you would have seen into a latency you will not, until memory runs out.
Nothing fails and nothing is refused, so the metric that would tell you reads zero. Meanwhile the head of the queue ages: depth divided by drain rate is how stale the oldest item is.
See every interleaving — 844 of 2,500 interleavings exceed the intended capacity.
How should you choose a queue bound?
Complexity derivation
From the latency you can tolerate: bound equals drain rate times acceptable delay.
A queue of 10,000 draining at 1,000 a second means the head is ten seconds old. Choose the delay you can accept, multiply by the drain rate, and check the memory fits — not the other way round.
What does a barrier guarantee, and what does it cost?
Explain it plainly
It makes a phase boundary real: nobody crosses until everybody arrives. It does not make anything faster — it adds waiting — and what it buys is that the second phase reads finished data. The cost is the maximum of the participants rather than the mean, which grows as you add workers.
No participant proceeds past it until every participant has arrived. The cost is the slowest participant, every phase, because everyone leaves at the moment the last one arrives.
See every interleaving — Without it, 1,866 of 2,500 interleavings read a partial result.
A phase-structured computation has no barrier and produces a wrong answer. Why is this hard to find?
Code diagnosis
The run completes normally with a plausible number and nothing reports a problem.
A worker reads a total that is still being assembled, computes on it and reports success. A race that crashes gets fixed; a race that returns a plausible number does not.
What is the difference between a latch and a barrier?
Comparison
A latch counts down once and stays open; a barrier resets and every arriver also waits.
A latch is one-shot and asymmetric: the threads counting down need not be the ones waiting. A barrier is reusable and symmetric: everyone who arrives waits, and it resets for the next phase.
A reader skips the read lock because "it is only a read". What can go wrong?
Code diagnosis
It can land mid-update and see a record that never existed.
Between the writer’s two stores the record is half updated. An unlocked reader can observe the new value in one field and the old in the other — a combination no thread ever wrote.
See every interleaving — 1,141 of 2,500 interleavings tear.
When does a reader-writer lock beat a plain mutex?
Trade-off & selection
When reads dominate heavily and the critical section is long enough to matter.
The read side is not free: incrementing a shared reader count is a write, so `n` readers generate `n` coherence events. Below roughly 90% reads, or for very short sections, a mutex usually wins.
Can a thread holding a read lock upgrade to the write lock?
Edge case reasoning
Generally no — it would have to wait for all readers to leave, including itself.
Java forbids upgrading outright and most other implementations deadlock. Downgrading — write lock to read lock — is safe and is the standard way to publish an update and keep reading it.
Why prefer a `BlockingQueue` or a channel over a hand-rolled condition variable?
Trade-off & selection
The predicate, the loop and the signalling are already correct in it.
Every mistake on this page — the `if`, the lost wakeup, the wrong acquisition order, signalling without changing state — is one the packaged construct has already made and fixed.