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 (']
');
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 /*
22 * If the alarm list is empty, wait until an alarm is
23 * added. Setting current_alarm to 0 informs the insert
24 * routine that the thread is not busy.
25 */
26 current_alarm = 0;
27 while (alarm_list == NULL) {
28 status = pthread_cond_wait (&alarm_cond, &alarm_mutex);
29 if (status != 0)
30 err_abort (status, 'Wait on cond');
31 }
32 alarm = alarm_list;
33 alarm_list = alarm->link;
34 now = time (NULL);
35 expired = 0;
36 if (alarm->time > now) {
37 #ifdef DEBUG
38 printf ('[waiting: %d(%d)'%s']
', alarm->time,
39 alarm->time - time (NULL), alarm->message);
40 #endif
41 cond_time.tv_sec = alarm->time;
42 cond_time.tv_nsec = 0;
43 current_alarm = alarm->time;
44 while (current_alarm == alarm->time) {
45 status = pthread_cond_timedwait (
46 &alarm_cond, &alarm_mutex, &cond_time);
47 if (Status == ETIMEDOUT) {
48 expired = 1;
49 break;