69 timeout.tv_nsec = 0;
70 status = pthread_mutex_lock (&data.mutex);
71 if (status != 0)
72 err_abort (status, 'Lock mutex');
73
74 while (data.value == 0) {
75 status = pthread_cond_timedwait (
76 &data.cond, &data.mutex, &timeout);
77 if (status == ETIMEDOUT) {
78 printf ('Condition wait timed out.
');
79 break;
80 }
81 else if (status != 0)
82 err_abort (status, 'Wait on condition');
83 }
84
85 if (data.value != 0)
86 printf ('Condition was signaled.
');
87 status = pthread_mutex_unlock (&data.mutex);
88 if (status != 0)
89 err_abort (status, 'Unlock mutex');
90 return 0;
91 }
There are a lot of reasons why it is a good idea to write code that does not assume the predicate is always true on wakeup, but here are a few of the main reasons:
Intercepted wakeups: Remember that threads are asynchronous. Waking up from a condition variable wait involves locking the associated mutex. But what if some other thread acquires the mutex first? It may, for example, be checking the predicate before waiting itself. It doesn't have to wait, since the predicate is now true. If the predicate is 'work available,' it will accept the work. When it unlocks the mutex there may be no more work. It would be expensive, and usually counterproductive, to ensure that the latest awakened thread got the work.
Loose predicates: For a lot of reasons it is often easy and convenient to use approximations of actual state. For example, 'there may be work' instead of 'there is work.' It is often much easier to signal or broadcast based on 'loose predicates' than on the real 'tight predicates.' If you always test the tight predicates before and after waiting on a condition variable, you're free to signal based on the loose approximations when that makes sense. And your code will be much more robust when a condition variable is signaled or broadcast accidentally. Use of loose predicates or accidental wakeups may turn out to be a performance issue; but in many cases it won't make a difference.
Spurious wakeups: This means that when you wait on a condition variable, the wait may (occasionally) return when no thread specifically broadcast or signaled that condition variable. Spurious wakeups may sound strange, but on some multiprocessor systems, making condition wakeup completely predictable might substantially slow all condition variable operations. The race conditions that cause spurious wakeups should be considered rare.
It usually takes only a few instructions to retest your predicate, and it is a good programming discipline. Continuing without retesting the predicate could lead to serious application errors that might be difficult to track down later. So don't make assumptions: Always wait for a condition variable in a while loop testing the predicate.
You can also use the pthread_cond_timedwait function, which causes the wait to end with an ETIMEDOUT status after a certain time is reached. The time is an absolute clock time, using the POSIX.1b struct timespec format. The timeout is absolute rather than an interval (or 'delta time') so that once you've computed the timeout it remains valid regardless of spurious or intercepted
wakeups. Although it might seem easier to use an interval time, you'd have to recompute it every time the thread wakes up, before waiting again—which would require determining how long it had already waited.
When a timed condition wait returns with the ETIMEDOUT error, you should test your predicate before treating the return as an error. If the condition for which you were waiting is true, the fact that it may have taken too long usually isn't important. Remember that a thread always relocks the mutex before returning from a condition wait, even when the wait times out. Waiting for a locked mutex after timeout can cause the timed wait to appear to have taken a lot longer than the time you requested.
3.3.3 Waking condition variable waiters
int pthread_cond_signal (pthread_cond_t *cond);
int pthread_cond_broadcast (pthread_cond_t *cond);
Once you've got a thread waiting on a condition variable for some predicate, you'll probably want to wake it up. Pthreads provides two ways to wake a condition variable waiter. One is called 'signal' and the other is called 'broadcast.' A signal operation wakes up a single thread waiting on the condition variable, while broadcast wakes up all threads waiting on the condition variable.
The term 'signal' is easily confused with the 'POSIX signal' mechanisms that allow you to define 'signal actions,' manipulate 'signal masks,' and so forth. However, the term 'signal,' as we use it here, had independently become well established in threading literature, and even in commercial implementations, and the Pthreads working group decided not to change the term. Luckily, there are few situations where we might be tempted to use both terms together—it is a very good idea to avoid using signals in threaded programs when at all possible. If we are careful to say 'signal a condition variable' or 'POSIX signal' (or 'UNIX signal') where there is any ambiguity, we are unlikely to cause anyone severe discomfort.
It is easy to think of 'broadcast' as a generalization of 'signal,' but it is more accurate to think of signal as an optimization of broadcast. Remember that it is never wrong to use broadcast instead of signal since waiters have to account for intercepted and spurious wakes. The only difference, in fact, is efficiency: A broadcast will wake additional threads that will have to test their predicate and resume waiting. But, in general, you can't replace a broadcast with a signal. 'When in doubt, broadcast.'
Use signal when only one thread needs to wake up to process the changed state, and when any waiting thread can do so. If you use one condition variable for several program predicate conditions, you can't use the signal operation: you couldn't tell whether it would awaken a thread waiting for that predicate, or for
another predicate. Don't try to get around that by resignaling the condition variable when you find the predicate isn't true. That might not pass on the signal as you expect; a spurious or intercepted wakeup could result in a series of pointless resignals.
If you add a single item to a queue, and only threads waiting for an item to appear are blocked on the condition variable, then you should probably use a signal. That'll wake up a single thread to check the queue and let the others sleep undisturbed, avoiding unnecessary context switches. On the other hand, if you add more than one item to the queue, you will probably need to broadcast. For examples of both broadcast and signal operations on condition variables, check out the 'read/write lock' package in Section 7.1.2.
Although you must have the associated mutex locked to wait on a condition variable, you can signal (or broadcast) a condition variable with the associated mutex unlocked if that is more convenient. The advantage of doing so is that, on many systems, this may be more efficient. When a waiting thread awakens, it must first lock the mutex. If the thread awakens while the signaling thread holds the mutex, then the awakened thread must immediately block on the mutex— you've gone through two context switches to get back where you started.
Weighing on the other side is the fact that, if the mutex is not locked, any thread (not only the one being awakened) can lock the mutex prior to the thread being awakened. This race is one source of intercepted wakeups.