It is important that you test the predicate after locking the appropriate mutex and before waiting on the condition variable. If a thread signals or broadcasts a condition variable while no threads are waiting, nothing happens. If some other thread calls pthread_cond_wait right after that, it will keep waiting regardless of the fact that the condition variable was just signaled, which means that if a thread waits when it doesn't have to, it may never wake up. Because the mutex remains locked until the thread is blocked on the condition variable, the predicate cannot become set between the predicate test and the wait—the mutex is locked and no other thread can change the shared data, including the predicate.
Always test your predicate; and then test it again!
It is equally important that you test the predicate again when the thread wakes up. You should always wait for a condition variable in a loop, to protect against program errors, multiprocessor races, and spurious wakeups. The following short program, cond.c, shows how to wait on a condition variable. Proper predicate loops are also shown in all of the examples in this book that use condition variables, for example, alarm_cond.c in Section 3.3.4.
20-37 The wait_thread sleeps for a short time to allow the main thread to reach its condition wait before waking it, sets the shared predicate (data.value), and then signals the condition variable. The amount of time for which wait_thread will sleep is controlled by the hibernation variable, which defaults to one second.
51-52 If the program was run with an argument, interpret the argument as an integer value, which is stored in hibernation. This controls the amount of time for which wait.thread will sleep before signaling the condition variable.
68-83 The main thread calls pthread_cond_timedwait to wait for up to two seconds (from the current time). If hibernation has been set to a value of greater than two seconds, the condition wait will time out, returning ETIMEDOUT. If hibernation has been set to two, the main thread and wait_thread race, and, in principle, the result could differ each time you run the program. If hibernation is set to a value less than two, the condition wait should not time out.
¦ cond.c
1 #include <pthread.h>
2 #include <time.h>
3 #include 'errors.h' 4
5 typedef struct my_struct_tag {
6 pthread_mutex_t mutex;
7 pthread_cond_t cond;
8 int value;
9 } my_struct_t;
10
11 my_struct_t data = {
12 PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, 0};
13
14 int hibernation = 1; /* Default to 1 second */
15
16 /*
17 * Thread start routine. It will set the main thread's predicate
18 * and signal the condition variable.
19 */
20 void *
21 wait_thread (void *arg)
22 {
23 int status;
24
25 sleep (hibernation);
26 status = pthread_mutex_lock (&data.mutex);
27 if (status != 0)
28 err_abort (status, 'Lock mutex');
29 data.value = 1; /* Set predicate */
30 status = pthread_cond_signal (&data.cond);
31 if (status != 0)
32 err_abort (status, 'Signal condition');
33 status = pthread_mutex_unlock (&data.mutex);
34 if (status != 0)
35 err_abort (status, 'Unlock mutex');
36 return NULL;
37 }
/* Protects access to value */ /* Signals change to value */ /* Access protected by mutex */
38
39 int main (int argc, char *argv[])
40 {
41 int status;
42 pthread_t wait_thread_id;
43 struct timespec timeout;
44
45 /*
46 * If an argument is specified, interpret it as the number
47 * of seconds for wait_thread to sleep before signaling the
48 * condition variable. You can play with this to see the
49 * condition wait below time out or wake normally.
50 */
51 if (argc > 1)
52 hibernation = atoi (argv[l]);
53
54 /*
55 * Create wait_thread.
56 */
57 status = pthread_create (
58 &wait_thread_id, NULL, wait_thread, NULL);
59 if (status != 0)
60 err_abort (status, 'Create wait thread');
61
62 /*
63 * Wait on the condition variable for 2 seconds, or until
64 * signaled by the wait_thread. Normally, wait_thread
65 * should signal. If you raise 'hibernation' above 2
66 * seconds, it will time out.
67 */
68 timeout.tv_sec = time (NULL) + 2;