27 fprintf (stderr, 'Bad command
');
28 free (alarm);
29 } else {
30 status = pthread_mutex_lock (&alarm_mutex);
31 if (status != 0)
32 err_abort (status, 'Lock mutex');
33 alarm->time = time (NULL) + alarm->seconds;
34
35 /*
36 * Insert the new alarm into the list of alarms,
37 * sorted by expiration time.
38 */
39 last = &alarm_list;
40 next = *last;
41 while (next != NULL) {
42 if (next->time >= alarm->time) {
43 alarm->link = next;
44 *last = alarm;
45 break;
46 }
47 last = &next->link;
48 next = next->link;
49 }
50 /*
51 * If we reached the end of the list, insert the new
52 * alarm there, ('next' is NULL, and 'last' points
53 * to the link field of the last item, or to the
54 * list header).
55 */
56 if (next == NULL) {
57 *last = alarm;
58 alarm->link = NULL;
59 }
60 #ifdef DEBUG
61 printf ('[list: ');
62 for (next = alarm_list; next != NULL; next = next->link)
63 printf ('%d(%d)['%s'] ', next->time,
64 next->time - time (NULL), next->message);
65 printf (']
');
66 #endif
67 status = pthread_mutex_unlock (&alarm_mutex);
68 if (status != 0)
69 err_abort (status, 'Unlock mutex');
70 }
71 }
72 }
This simple program has a few severe failings. Although it has the advantage, compared to alarm_fork. c or alarm_thread.c, of using fewer resources, it is less responsive. Once alarm_thread has accepted an alarm request from the queue, it
sleeps until that alarm expires. When it fails to find an alarm request on the list, it sleeps for a second anyway, to allow main to accept another alarm command. During all this sleeping, it will fail to notice any alarm requests added to the head of the queue by main, until it returns from sleep.
This problem could be addressed in various ways. The simplest, of course, would be to go back to alarm_thread.c, where a thread was created for each alarm request. That wasn't so bad, since threads are relatively cheap. They're still not as cheap as the alarm_t data structure, however, and we'd like to make efficient programs—not just responsive programs. The best solution is to make use of condition variables for signaling changes in the state of shared data, so it shouldn't be a surprise that you'll be seeing one final version of the alarm program, alarm_cond.c, in Section 3.3.4.
3.2.2.1 Nonblocking mutex locks
When you lock a mutex by calling pthread_mutex_lock, the calling thread will block if the mutex is already locked. Normally, that's what you want. But occasionally you want your code to take some alternate path if the mutex is locked. Your program may be able to do useful work instead of waiting. Pthreads provides the pthread_mutex_trylock function, which will return an error status (EBUSY) instead of blocking if the mutex is already locked.
When you use a nonblocking mutex lock, be careful to
The following program, trylock.c, uses pthread_mutex_trylock to occasionally report the value of a counter—but only when its access does not conflict with the counting thread.
4 This definition controls how long counter_thread holds the mutex while updating the counter. Making this number larger increases the chance that the pthread_mutex_trylock in monitor_thread will occasionally return EBUSY.
14-39 The counter_thread wakes up approximately each second, locks the mutex, and spins for a while, incrementing counter. The counter is therefore increased by SPIN each second.
46-72 The monitor_thread wakes up every three seconds, and tries to lock the mutex. If the attempt fails with EBUSY, monitor_thread counts the failure and waits another three seconds. If the pthread_mutex_trylock succeeds, then monitor_thread prints the current value of counter (scaled by SPIN).
80-88 On Solaris 2.5, call thr_setconcurrency to set the thread concurrency level to 2. This allows the counter_thread and monitor_thread to run concurrently on a uniprocessor. Otherwise, monitor_thread would not run until counter_ thread terminated.
¦ trylock.c
1 #include <pthread.h>
2 #include 'errors.h' 3
4 #define SPIN 10000000 5
6 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
7 long counter;
8 time_t end_time;
9
10 /*
11 * Thread start routine that repeatedly locks a mutex and
12 * increments a counter.
13 */
14 void *counter_thread (void *arg)