Книга: Programming with POSIX® Threads
5.3.1 Deferred cancelability
5.3.1 Deferred cancelability
"Deferred cancelability" means that the thread's cancelability type has been set to PTHREAD_CANCEL_DEFERRED and the thread's cancelability enable has been set to PTHREAD_CANCEL_ENABLE. The thread will only respond to cancellation requests when it reaches one of a set of "cancellation points."
The following functions are always cancellation points on any Pthreads system:
pthread_cond_wait fsync
pthread_cond_timedwait mq_receive pthread_join mq_send
pthread_testcancel msync
sigwait nanosleep
aio_suspend open
close pause
creat read
fcntl (F_SETLCKW) sem_wait
The following list of functions may be cancellation points. You should write your code so that it will function correctly if any of these are cancellation points
sigwaitinfo
sigsuspend
sigtimedwait
sleep
system
tcdrain
wait
waitpid
write
and also so that it will not break if any of them are not. If you depend upon any particular behavior, you may limit the portability of your code. You'll have to look at the conformance documentation to find out which, if any, are cancellation points for the system you are using:
closedir | getc_unlocked | printf |
ctermid | getchar | putc |
fclose | getchar_unlocked | putc_unlocked |
fcntl (except F_SETLCKW) | getcwd | putchar |
fflush | getgrgid | putchar_unlocked |
fgetc | getgrgid_r | puts |
fgets | getrtnam | readdir |
fopen | getgrnam_r | remove |
fprintf | getlogin | rename |
fputc | getlogin_r | rewind |
fputs | getpwnam | rewinddir |
fread | getpwnam_r | scanf |
freopen | getpwuid | tmpfile |
fscanf | getpwuid_r | tmpname |
fseek | gets | ttyname |
ftell | lseek | ttyname_r |
fwrite | opendir | ungetc |
getc | perror |
Pthreads specifies that any ANSI C or POSIX function not specified in one of the two lists cannot be a cancellation point. However, your system probably has many additional cancellation points. That's because few UNIX systems are "POSIX." That is, they support other programming interfaces as well — such as BSD 4.3, System V Release 4, UNLX95, and so forth. POSIX doesn't recognize the existence of functions such as select or poll, and therefore it can't say whether or not they are cancellation points. Yet clearly both are functions that may block for an arbitrary period of time, and programmers using them with cancellation would reasonably expect them to behave as cancellation points. X/Open is currently addressing this problem for UNLX98 (X/Open System Interfaces, Issue 5). by extending the Pthreads list of cancellation points.
Most cancellation points involve I/O operations that may block the thread for an "unbounded" time. They're cancelable so that the waits can be interrupted. When a thread reaches a cancellation point the system determines whether a cancel is pending for the current ("target") thread. A cancel will be pending if another thread has called pthread_cancel for the target thread since the last time the target thread returned from a cancellation point. If a cancel is pending, the system will immediately begin calling cleanup functions, and then the thread will terminate.
If no cancel is currently pending, the function will proceed. If another thread requests that the thread be canceled while the thread is waiting for something (such as I/O) then the wait will be interrupted and the thread will begin its cancellation cleanup.
If you need to ensure that cancellation can't occur at a particular cancellation point, or during some sequence of cancellation points, you can temporarily disable cancellation in that region of code. The following program, called cancel_ disable.c, is a variant of cancel.c. The "target" thread periodically calls sleep, and does not want the call to be cancelable.
23-32 After each cycle of 755 iterations, thread_routine will call sleep to wait a second. (The value 755 is just an arbitrary number that popped into my head. Do arbitrary numbers ever pop into your head?) Prior to sleeping, thread_routine disables cancellation by setting the cancelability state to PTHREAD_CANCEL_ DISABLE. After sleep returns, it restores the saved cancelability state by calling pthread_setcancelstate
again.
33-35 Just as in cancel.c, test for a pending cancel every 1000 iterations.
? cancel_disable.c
1 #include <pthread.h>
2 #include "errors.h"
3
4 static int counter;
5
6 /*
7 * Thread start routine.
8 */
9 void *thread_routine (void *arg)
10 {
11 int state;
12 int status;
13
14 for (counter = 0; ; counter++) {
15
16 /*
17 * Each 755 iterations, disable cancellation and sleep
18 * for one second.
19 *
20 * Each 1000 iterations, test for a pending cancel by
21 * calling pthread_testcancel().
22 */
23 if ((counter % 755) == 0) {
24 status = pthread_setcancelstate (
25 PTHREAD_CANCEL_DISABLE, &state);
26 if (status != 0)
27 err_abort (status, "Disable cancel");
28 sleep (1);
29 status = pthread_setcancelstate (
30 state, &state);
31 if (status != 0)
32 err_abort (status, "Restore cancel");
33 } else
34 if ((counter % 1000) == 0)
35 pthread_testcancel ();
36 }
37 }
38
39 int main (int argc, char *argv[])
40 {
41 pthread_t thread_id;
42 void *result;
43 int status;
44
45 status = pthread_create (
46 &thread_id, NULL, thread_routine, NULL);
47 if (status != 0)
48 err_abort (status, "Create thread");
49 sleep (2);
50 status = pthread_cancel (thread_id);
51 if (status != 0)
52 err_abort (status, "Cancel thread");
53
54 status = pthread_join (thread_id, &result);
55 if (status != 0)
56 err_abort (status, "Join thread");
57 if (result == PTHREAD_CANCELED)
58 printf ("Thread canceled at iteration %dn", counter);
59 else
60 printf ("Thread was not canceledn");
61 return 0;
62 }