In the previous section, we used pthread_cond_wait, which puts a thread to sleep indefinitely. If the signal never comes (e.g., the producer crashes), the consumer sleeps forever.
This section introduces two tools to make your applications more robust: Timed Waits (setting a deadline) and Broadcasts (waking everyone up).
1. Timed Waits (pthread_cond_timedwait)
This function allows you to wait for a signal, but with an automatic “timeout” if the signal doesn’t arrive by a certain time.
int pthread_cond_timedwait(pthread_cond_t *cptr, pthread_mutex_t *mptr,
const struct timespec *abstime);The “Absolute Time” Trap
The most critical technical detail in this section—and the most common bug for beginners—is that the time argument is absolute, not relative.
- Relative Time (Wrong): “Wait for 5 seconds.”
- Absolute Time (Correct): “Wait until 10:00:05 AM.”
If you pass “5” to this function, it interprets it as “5 seconds after the Epoch” (which was in 1970), so the function will timeout immediately!
Correct Pattern for Waiting 5 Seconds: To wait 5 seconds, you must get the current time and add 5 seconds to it.
struct timespec ts;
struct timeval tv;
// 1. Get current time
gettimeofday(&tv, NULL);
// 2. Set the "deadline" (current time + 5 seconds)
ts.tv_sec = tv.tv_sec + 5;
ts.tv_nsec = tv.tv_usec * 1000; // Convert microseconds to nanoseconds
// 3. Wait
pthread_mutex_lock(&mutex);
while (ready == 0) {
int rc = pthread_cond_timedwait(&cond, &mutex, &ts);
if (rc == ETIMEDOUT) {
// Handle timeout (we woke up because time ran out, not because of a signal)
break;
}
}
pthread_mutex_unlock(&mutex);2. Broadcasts (pthread_cond_broadcast)
Up until now, we have used pthread_cond_signal, which wakes up one waiting thread. pthread_cond_broadcast wakes up all threads currently waiting on that condition variable.
When to use Broadcast vs. Signal?
| Feature | pthread_cond_signal | pthread_cond_broadcast |
|---|---|---|
| Behavior | Unblocks one thread. | Unblocks all threads. |
| Efficiency | High (avoids “Thundering Herd”). | Lower (everyone wakes up and fights for the lock). |
| Use Case | Producer-Consumer: One item produced = One consumer needed. | Reader-Writer Locks: A writer finishes and multiple readers can now read safely. |
| State Change | The state change allows only one thread to proceed. | The state change allows multiple threads to proceed. |
The “Thundering Herd” Problem
Stevens warns that using broadcast when signal would suffice causes a performance issue known as the Thundering Herd.
- You have 100 threads sleeping.
- You call
broadcast. - All 100 threads wake up and try to grab the same mutex.
- One wins; the other 99 immediately go back to sleep (blocked on the mutex).
- This wastes massive amounts of CPU time context-switching.
Rule of Thumb: Use signal unless you specifically know that multiple threads need to wake up simultaneously.