a condition variable
A mutex answers 'may I touch this data?' A condition variable answers a different question: 'I am holding the lock, but the data is not in the state I need yet — how do I sleep until someone makes it ready, without hogging the lock while I wait?' Picture a quiet waiting room next to the locked room: a thread that finds the world not ready steps into the waiting room, releasing the lock, and naps until someone taps it on the shoulder.
A condition variable always works together with a mutex. Its core operation is wait: while holding the mutex, you call pthread_cond_wait(&cv, &m), which atomically releases the mutex and puts the thread to sleep, all in one indivisible step (so it cannot miss a wakeup that happens in the crack between). When the thread is later woken, wait re-acquires the mutex before returning, so you are back inside the critical section. Another thread changes the shared state, then calls signal to wake one waiter, or broadcast to wake all of them. The non-negotiable pattern is to wait inside a loop that re-checks the predicate: while (!ready) pthread_cond_wait(&cv, &m); — never a plain if. After waking you must re-test the condition, because by the time you re-acquire the lock the state may have changed again, and because of spurious wakeups.
Condition variables are how threads wait for an event rather than for a lock: a queue that is empty, a buffer that is full, a job that has finished. They are the standard building block for the producer-consumer pattern and for any 'wait until some condition becomes true' situation. The classic bug is signalling without holding the lock or without first updating the state, so a waiter checks the predicate, finds it false, and goes to sleep in the exact instant the signal fires — a lost wakeup that hangs forever.
Consumer: lock(m); while (count == 0) cond_wait(¬Empty, &m); take_item(); unlock(m);. Producer: lock(m); put_item(); cond_signal(¬Empty); unlock(m);. The while loop, not an if, is what makes it correct.
Wait atomically drops the lock and sleeps; always re-check the predicate in a while loop on wakeup.
A condition variable holds no state and remembers no signals: a signal sent when nobody is waiting is simply lost. That is exactly why you update the shared predicate under the lock first, and why the waiter loops on the predicate rather than trusting the signal.