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?

Featurepthread_cond_signalpthread_cond_broadcast
BehaviorUnblocks one thread.Unblocks all threads.
EfficiencyHigh (avoids “Thundering Herd”).Lower (everyone wakes up and fights for the lock).
Use CaseProducer-Consumer: One item produced = One consumer needed.Reader-Writer Locks: A writer finishes and multiple readers can now read safely.
State ChangeThe 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.

  1. You have 100 threads sleeping.
  2. You call broadcast.
  3. All 100 threads wake up and try to grab the same mutex.
  4. One wins; the other 99 immediately go back to sleep (blocked on the mutex).
  5. 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.