37 for (next = alarm_list; next != NULL; next = next->link)
38 printf ("%d(%d)[\"%s\"] ", next->time,
39 next->time - time (NULL), next->message);
40 printf ("]\n");
41 #endif
42 /*
43 * Wake the alarm thread if it is not busy (that is, if
44 * current_alarm is 0, signifying that it's waiting for
45 * work), or if the new alarm comes before the one on
46 * which the alarm thread is waiting.
47 */
48 if (current_alarm == 0 || alarm->time < current_alarm) {
49 current_alarm = alarm->time;
50 status = pthread_cond_signal (&alarm_cond);
51 if (status != 0)
52 err_abort (status, "Signal cond");
53 }
54 }
Part 3 shows the alarm_thread function, the start function for the "alarm server" thread. The general structure of alarm_thread is very much like the alarm_thread in alarm_mutex.c. The differences are due to the addition of the condition variable.
26-31 If the alarm_list is empty, alarm_mutex.c could do nothing but sleep anyway, so that main would be able to process a new command. The result was that it could not see a new alarm request for at least a full second. Now, alarm_thread instead waits on the alarm_cond condition variable, with no timeout. It will "sleep" until you enter a new alarm command, and then main will be able to awaken it immediately. Setting current_alarm to 0 tells main that alarm_thread is idle. Remember that pthread_cond_wait unlocks the mutex before waiting, and relocks the mutex before returning to the caller.
35 The new variable expired is initialized to 0; it will be set to 1 later if the timed condition wait expires. This makes it a little easier to decide whether to print the current alarm's message at the bottom of the loop.
36-42 If the alarm we've just removed from the list hasn't already expired, then we need to wait for it. Because we're using a timed condition wait, which requires a POSIX.1b struct timespec, rather than the simple integer time required by sleep, we convert the expiration time. This is easy, because a struct timespec has two members—tv_sec is the number of seconds since the Epoch, which is exactly what we already have from the time function, and tv_nsec is an additional count of nanoseconds. We will just set tv_nsec to 0, since we have no need of the greater resolution.
43 Record the expiration time in the current_alarm variable so that main can determine whether to signal alarm_cond when a new alarm is added.
44-53 Wait until either the current alarm has expired, or main requests that alarm_ thread look for a new, earlier alarm. Notice that the predicate test is split here, for convenience. The expression in the while statement is only half the predicate, detecting that main has changed current_alarm by inserting an earlier timer. When the timed wait returns ETIMEDOUT, indicating that the current alarm has expired, we exit the while loop with a break statement at line 49.
54-55 If the while loop exited when the current alarm had not expired, main must have asked alarm_thread to process an earlier alarm. Make sure the current alarm isn't lost by reinserting it onto the list.
57 If we remove from alarm_list an alarm that has already expired, just set the expired variable to 1 to ensure that the message is printed.
■ alarm_cond.c part 3 alarm_routine
1 /*
2 * The alarm thread's start routine.
3 */
4 void *alarm_thread (void *arg)
5 {
6 alarm_t *alarm;
7 struct timespec cond_time;
8 time_t now;
9 int status, expired;
10
11 /*
12 * Loop forever, processing commands. The alarm thread will
13 * be disintegrated when the process exits. Lock the mutex
14 * at the start — it will be unlocked during condition
15 * waits, so the main thread can insert alarms.
16 */
17 status = pthread_mutex_lock (&alarm_mutex);
18 if (status != 0)
19 err_abort (status, "Lock mutex");
20 while (1) {
21 /*