69 * that our two threads can run concurrently, we need to
70 * increase the concurrency level to 2.
71 */
72 DPRINTF (("Setting concurrency level to 2\n"));
73 thr_setconcurrency (2);
74 #endif
75 status = pthread_create (
76 &thread_id, NULL, thread_routine, NULL);
77 if (status != 0)
78 err_abort (status, "Create thread");
79 sleep (1);
80 status = pthread_cancel (thread_id);
81 if (status != 0)
82 err_abort (status, "Cancel thread");
83 status = pthread_join (thread_id, &result);
84 if (status != 0)
85 err_abort (status, "Join thread");
86 if (result == PTHREAD_CANCELED)
87 printf ("Thread canceled\n");
88 else
89 printf ("Thread was not canceled\n");
90 return 0;
91 }
Warning: do not let "DCE threads'" habits carry over to Pthreads!
I'll end this section with a warning. DCE threads, a critical component of the Open Software Foundation's Distributed Computing Environment, was designed to be independent of the underlying UNIX kernel. Systems with no thread support at all often emulated "thread synchronous" I/O in user mode, using nonblocking I/O mode, so that a thread attempting I/O on a busy file was blocked on a condition variable until a later select or poll showed that the I/O could complete. DCE listener threads might block indefinitely on a socket read, and it was important to be able to cancel that read.
When DCE was ported to newer kernels that had thread support, but not Pthreads support, the user mode I/O wrappers were usually omitted, resulting in a thread blocked within a kernel that did not support deferred 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.