10-17 The control structure (control) is used by all threads to maintain shared synchronization objects and invariants. Each thread increases the member counter by one when it starts, and decreases it at termination. The member busy is used as a dummy condition wait predicate — it is initialized to 1, and never cleared, which means that the condition wait loops will never terminate (in this example) until the threads are canceled.
24-34 The function cleanup_handler is installed as the cancellation cleanup handler for each thread. It is called on normal termination as well as through cancellation, to decrease the count of active threads and unlock the mutex.
47 The function thread_routine establishes cleanup_handler as the active cancellation cleanup handler.
54-58 Wait until the control structure's busy member is set to 0, which, in this example, will never occur. The condition wait loop will exit only when the wait is canceled.
60 Although the condition wait loop in this example will not exit, the function cleans up by removing the active cleanup handler. The nonzero argument to pthread_cleanup_pop
, remember, means that the cleanup handler will be called even though cancellation did not occur.
In some cases, you may omit "unreachable statements" like this pthread_cleanup_pop
call. However, in this case, your code might not compile without it. The pthread_cleanup_push
and pthread_cleanup_pop
macros are special, and may expand to form, respectively, the beginning and ending of a block. Digital UNIX does this, for example, to implement cancellation on top of the common structured exception handling provided by the operating system.
■ cancel_cleanup.c
1 #include
2 #include "errors.h"
3
4 #define THREADS 5
5
6 /*
7 * Control structure shared by the test threads, containing
8 * the synchronization and invariant data.
9 */
10 typedef struct control_tag {
11 int counter, busy;
12 pthread_mutex_t mutex;
13 pthread_cond_t cv;
14 } control_t;
15
16 control_t control =
17 {0, 1, PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER};
18
19 /*
20 * This routine is installed as the cancellation cleanup
21 * handler around the cancelable condition wait. It will
22 * be called by the system when the thread is canceled.
23 */
24 void cleanup_handler (void *arg)
25 {
26 control_t *st = (control_t *)arg;
27 int status;
28
29 st->counter--;
30 printf ("cleanup_handler: counter == %d\n", st->counter);
31 status = pthread_mutex_unlock (&st->mutex);
32 if (status != 0)
33 err_abort (status, "Unlock in cleanup handler");
34 }
35
36 /*
37 * Multiple threads are created running this routine (controlled
38 * by the THREADS macro). They maintain a "counter" invariant,
39 * which expresses the number of running threads. They specify a
40 * nonzero value to pthread_cleanup_pop to run the same
41 * "finalization" action when cancellation does not occur.
42 */
43 void *thread_routine (void *arg)
44 {
45 int status;
46
47 pthread_cleanup_push (cleanup_handler, (void*)&control);
48
49 status = pthread_mutex_lock (&control.mutex);
50 if (status != 0)
51 err_abort (status, "Mutex lock");
52 control.counter++;
53
54 while (control.busy) {
55 status = pthread_cond_wait (&control.cv, &control.mutex);
56 if (status != 0)
57 err_abort (status, "Wait on condition");
58 }
59
60 pthread_cleanup_pop (1);