← Interlude: Thread API
Locks & condition variables: the pthread API § 27.4–27.5 · pp. 295-300

Last stretch: the synchronization primitives. We’ll hit pthread_mutex_* and pthread_cond_* and make sure you remember why condition variables always pair with a mutex.

Q1 of 5

Why must pthread_cond_wait() be called while holding a mutex?

So the wait and unlock happen atomically

Correct

Right — cond_wait atomically releases the mutex and sleeps. Without that atomicity you’d race against signals between the check and the wait.

Q2 of 5

What’s the practical difference between pthread_cond_signal and pthread_cond_broadcast?

Signal wakes one waiter, broadcast wakes all of them

Correct

Exactly. Use broadcast when you can’t reason about which one waiter should wake up — e.g., resources become available in batches.

Q3 of 5

Why must you check the predicate in a while loop around cond_wait, not an if?

Because spurious wakeups happen — cond_wait can return without a signal

Correct

Right. The kernel is allowed to wake waiters spuriously. Even without that, between the wake and re-acquiring the mutex, another waiter could grab whatever you were waiting for. Always re-check.

Q4 of 5

What’s a spurious wakeup?

When pthread_cond_wait returns even though nobody called signal or broadcast — a side effect of the kernel implementation that you have to defensively handle.

Correct

Exactly. POSIX explicitly allows it. That’s why predicate-in-a-loop is the standard pattern: the wakeup is just an invitation to re-check, not a guarantee.

Q5 of 5

When would you reach for a condition variable instead of just polling the predicate in a tight loop?

When the wait could be long — polling burns CPU; cond_wait deschedules the thread so the OS can run something else until a signal wakes it.

Correct

Yep. Polling is a CPU bonfire. Condition variables let the thread sleep efficiently and only wake when there’s actually something to do.

Nice work — 5 of 5 correct. Re-study any time to retry the same questions.

Locks & condition variables: the pthread API

Mutexes

sequenceDiagram
  participant T1 as Thread 1
  participant M as Mutex
  participant T2 as Thread 2
  T1->>M: pthread_mutex_lock()
  M-->>T1: acquired
  T2->>M: pthread_mutex_lock()
  Note right of M: T2 blocks
  T1->>M: pthread_mutex_unlock()
  M-->>T2: acquired
  • Lock before touching shared state, unlock right after.
  • pthread_mutex_lock blocks until the lock is yours; _trylock returns immediately with EBUSY if it’s held.
  • Forgetting to unlock is the classic deadlock cause.

Condition variables

  • A condition variable is a sleep-and-wake primitive: “wait until someone tells me something interesting happened.”
  • Always paired with a mutex. The protected predicate is what “something interesting” means — e.g., queue_nonempty.
pthread_mutex_lock(&m);
while (!ready) {            // loop, not if
  pthread_cond_wait(&cv, &m);
}
/* now safe to consume */
pthread_mutex_unlock(&m);
  • pthread_cond_wait(&cv, &m) atomically:
    1. Releases &m,
    2. Puts the thread to sleep on &cv,
    3. Re-acquires &m on wake-up before returning.

signal vs broadcast

  • pthread_cond_signal wakes one waiter — use when exactly one waiter can make progress per signal (typical producer/consumer).
  • pthread_cond_broadcast wakes all waiters — use when the predicate change might unblock multiple, or when you can’t reason about which one should wake.

Spurious wakeups

  • POSIX explicitly allows cond_wait to return without anyone signalling. The kernel and pthread implementation are free to wake waiters whenever.
  • Even without spurious wakeups, between wake and re-acquiring the mutex, another waiter could grab the thing you wanted.
  • Loop-check the predicate, every time. The wakeup is an invitation to re-check, not a guarantee.

Quiz yourself

  • Why does cond_wait require the mutex to be held?
  • Why must the predicate check be in a while loop, not an if?