24 * command. If the list is not empty, remove the first
25 * item. Compute the number of seconds to wait — if the
26 * result is less than 0 (the time has passed), then set
27 * the sleep_time to 0.
28 */
29 if (alarm == NULL)
30 sleep_time = 1;
31 else {
32 alarm_list = alarm->link;
33 now = time (NULL);
34 if (alarm->time <= now)
35 sleep_time = 0;
36 else
37 sleep_time = alarm->time - now;
38 #ifdef DEBUG
39 printf ('[waiting: %d(%d)'%s']
', alarm->time,
40 sleep_time, alarm->message);
41 #endif
42 }
43
44 /*
45 * Unlock the mutex before waiting, so that the main
46 * thread can lock it to insert a new alarm request. If
47 * the sleep_time is 0, then call sched_yield, giving
48 * the main thread a chance to run if it has been
49 * readied by user input, without delaying the message
50 * if there's no input.
51 */
52 status = pthread_mutex_unlock (&alarm_mutex);
53 if (status != 0)
54 err_abort (status, 'Unlock mutex');
55 if (sleep_time > 0)
56 sleep (sleep_time);
57 else
58 sched_yield ();
59
60 /*
61 * If a timer expired, print the message and free the
62 * structure.
63 */
64 if (alarm != NULL) {
65 printf ('(%d) %s
', alarm->seconds, alarm->message);
66 free (alarm);
67 }
68 }
69 }
And finally, the code for the main program for alarm_mutex.c. The basic structure is the same as all of the other versions of the alarm program that we've developed—a loop, reading simple commands from stdin and processing each in turn. This time, instead of waiting synchronously as in alarm.c, or creating a new asynchronous entity to process each alarm command as in alarm_fork.c and alarm_thread.c, each request is queued to a server thread, alarm_thread. As soon as main has queued the request, it is free to read the next command.
8-11 Create the server thread that will process all alarm requests. Although we don't use it, the thread's ID is returned in local variable thread.
13-28 Read and process a command, much as in any of the other versions of our alarm program. As in alarm_thread.c, the data is stored in a heap structure allocated by malloc.
30-32 The program needs to add the alarm request to alarm_list, which is shared by both alarm_thread and main. So we start by locking the mutex that synchronizes access to the shared data, alarm_mutex.
33 Because alarm_thread processes queued requests, serially, it has no way of knowing how much time has elapsed between reading the command and processing it. Therefore, the alarm structure includes the absolute time of the alarm expiration, which we calculate by adding the alarm interval, in seconds, to the
current number of seconds since the UNIX Epoch, as returned by the time function.
39-49 The alarms are sorted in order of expiration time on the alarm_list queue. The insertion code searches the queue until it finds the first entry with a time greater than or equal to the new alarm's time. The new entry is inserted preceding the located entry. Because alarm_list is a simple linked list, the traversal maintains a current entry pointer (this) and a pointer to the previous entry's link member, or to the alarm_list head pointer (last).
56-59 If no alarm with a time greater than or equal to the new alarm's time is found, then the new alarm is inserted at the end of the list. That is, if the alarm pointer is NULL on exit from the search loop (the last entry on the list always has a link pointer of NULL), the previous entry (or queue head) is made to point to the new entry.
¦ alarm_mutex.c part 3 main
1 int main (int argc, char *argv[])
2 {
3 int status;
4 char line[128];
5 alarm_t *alarm, **last, *next;
6 pthread_t thread;
7
8 status = pthread_create (
9 &thread, NULL, alarm_thread, NULL);
10 if (status != 0)
11 err_abort (status, 'Create alarm thread');
12 while (1) {
13 printf ('alarm> ');
14 if (fgets (line, sizeof (line), stdin) == NULL) exit (0);
15 if (strlen (line) <= 1) continue;
16 alarm = (alarm_t*)malloc (sizeof (alarm_t));
17 if (alarm == NULL)
18 errno_abort ('Allocate alarm');
19
20 /*
21 * Parse input line into seconds (%d) and a message
22 * (%64[^
]), consisting of up to 64 characters
23 * separated from the seconds by whitespace.
24 */
25 if (sscanf (line, '%d %64[^
]',
26 &alarm->seconds, alarm->message) < 2) {