cancellation. Users discovered that, in many cases, these systems implemented asynchronous cancellation in such a way that, quite by coincidence, a kernel wait might be canceled 'safely' if the thread switched to asynchronous cancellation immediately before the kernel call, and switched back to deferred cancellation immediately after. This observation was publicized in DCE documentation, but it is a very dangerous hack, even on systems where it seems to work. You should
5.3.3 Cleaning up
When you write any library code, design it to handle deferred cancellation gracefully. Disable cancellation where it is not appropriate,and always use cleanup handlers at cancellation points.
If a section of code needs to restore some state when it is canceled, it must use cleanup handlers. When a thread is canceled while waiting for a condition variable, it will wake up with the mutex locked. Before the thread terminates it usually needs to restore invariants, and it always needs to release the mutex.
Each thread may be considered to have a stack of active cleanup handlers. Cleanup handlers are added to the stack by calling pthread_cleanup_push, and the most recently added cleanup handler is removed by calling pthread_cleanup_ pop. When the thread is canceled or when it exits by calling pthread_exit, Pthreads calls each active cleanup handler in turn, beginning with the most recently added cleanup handler. When all active cleanup handlers have returned, the thread is terminated.
Pthreads cleanup handlers are designed so that you can often use the cleanup handler even when the thread wasn't canceled. It is often useful to run the same cleanup function regardless of whether your code is canceled or completes normally. When pthread_cleanup_pop is called with a nonzero value, the cleanup handler is executed even ifthe thread was not canceled.
You cannot push a cleanup handler in one function and pop it in another function. The pthread_cleanup_push and pthread_cleanup_pop operations may be defined as macros, such that pthread_cleanup_push contains the opening brace '{' of ablock, while pthread_cleanup_pop contains the matching closing brace '}' of the block. You must always keep this restriction in mind while using cleanup handlers, if you wish your code to be portable.
The following program, cancel_cleanup.c, shows the use of a cleanup handler to release a mutex when a condition variable wait is canceled.
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 <pthread.h>
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
', 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;