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_lockblocks until the lock is yours;_trylockreturns immediately withEBUSYif 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:- Releases
&m, - Puts the thread to sleep on
&cv, - Re-acquires
&mon wake-up before returning.
- Releases
signal vs broadcast
pthread_cond_signalwakes one waiter — use when exactly one waiter can make progress per signal (typical producer/consumer).pthread_cond_broadcastwakes 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_waitto 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
whileloop, not anif?