Home | Projects | Notes > Multi-Threading (POSIX Threads) > Thread Cancellation - Deferred Cancellation
Deferred cancellation allows the programmer to control as to which points in the execution flow of the thread, the thread is allowed to be canceled (terminated).
It is contrary to asynchronous cancellation where thread could be canceled at any point in its execution flow.
Deferred cancellation is used to handle the problem of invariants caused by an abrupt termination of a thread while it is carrying out:
An operation to modify a data structure
A function call or a system call
If a thread gets canceled while it is in the middle of processing a system call whose execution takes place in the kernel space, then the kernel might get corrupted.
Asynchronous cancellation could not handle invariants issues!
Cancel signal can be delivered by the kernel to the target thread at any time, but it is processed only at the designated cancellation points of the target thread.
It is the programmer's responsibility to assign a cancellation point wisely so that when the thread terminates at its cancellation point no variants, resource leaks and deadlocks occur.
The API pthread_testcancel()
is used to do that. It tests if cancel signal is pending, and if yes, it invokes the cleanup handlers and then cancels (terminates) the thread.
The only update made to the asynchrnous_cancellation.c
file to turn it into a perfectly cancellation safe deferred_cancellation.c
file is the line 82; adding the cancellation point using pthread_testcancel()
.
xxxxxxxxxx
1291/*
2 * File Name : deferred_cancellation.c
3 * Description : C program to demonstrate pthread deferred cancellation
4 * Author : Modified by Kyungjae Lee
5 * (Original: Abhishek Sagar - Juniper Networks)
6 * Date Created : 01/07/2023
7 */
8
9
10
11
12
13
14
15
16
17pthread_t slaves[N_SLAVES]; /* array of thread handles */
18
19/* thread cleanup handler to prevent resource (memory) leak */
20void memory_cleanup_handler(void *arg)
21{
22 printf("%s invoked...\n", __FUNCTION__);
23 free(arg);
24}
25
26/* thread cleanup handler to prevent resource (file) leak */
27void file_cleanup_handler(void *arg)
28{
29 printf("%s invoked...\n", __FUNCTION__);
30 close((FILE*)arg);
31}
32
33/* thread function */
34void* write_into_file(void *arg)
35{
36 char file_name[64];
37 char string_to_write[64];
38 int len;
39 int count = 0;
40
41 /* set the thread eligible for cancellation */
42 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE /* PTHREAD_CANCEL_DISABLE */, 0);
43 /* set cancellation mode to ASYNCHRONOUS */
44 pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, 0);
45 /* Notice that this two APIs do not take the thread handle as an argument. This
46 * implies that the thread that invokes these APIs can only affect itself. In
47 * other words, no thread can set the 'cancelstate' or the 'canceltype' of another
48 * thread. */
49
50 int *thread_id = (int*)arg;
51
52 /* register the cleanup handler for the dynamically allocated variable 'arg' */
53 pthread_cleanup_push(memory_cleanup_handler, arg);
54
55 sprintf(file_name, "thread_%d.txt", *thread_id);
56
57 FILE *fptr = fopen(file_name, "w");
58
59 if(!fptr)
60 {
61 printf("Error : Could not open log file %s, errno = %d\n", file_name, errno);
62 pthread_exit(0); /* Make sure to terminate the program using this API so the cleanup handlers
63 can be invoked at the termination. Terminating using 'return' statement
64 will not invoke the cleanup handlers */
65 /* return 0; */
66 }
67
68 /* register the cleanup handler for the opened file pointed to by 'fptr' */
69 pthread_cleanup_push(file_cleanup_handler, fptr);
70
71 int a = 0;
72
73 while(a < 20)
74 {
75 len = sprintf(string_to_write, "%d : I am thread %d\n", count++, *thread_id);
76 fwrite(string_to_write, sizeof(char), len, fptr);
77 fflush(fptr);
78 sleep(1);
79 a++;
80
81 /* cancellation point (deferred cancellation) */
82 pthread_testcancel();
83 }
84
85 /* pop the cleanup handlers out of stack */
86 pthread_cleanup_pop(1); /* arg (non-0: pop cleanup function and invoke it, 0: just pop) */
87 pthread_cleanup_pop(1);
88
89 return 0;
90}
91
92int main(int argc, char **argv)
93{
94 int i;
95 int *thread_id = 0;
96
97 for (i = 0; i < N_SLAVES; i++){
98 thread_id = calloc(1, sizeof(*thread_id));
99 *thread_id = i;
100 pthread_create(&slaves[i], 0, write_into_file, thread_id);
101 }
102
103 /* main menu */
104 int choice;
105 int thread_num;
106
107 while(1) {
108
109 printf("1. Cancel the thread\n");
110 scanf("%d", &choice);
111 printf("Enter slave thread id [0-%d] :", N_SLAVES -1);
112 scanf("%d", &thread_num);
113 if(thread_num < 0 || thread_num >= N_SLAVES) {
114 printf("Invalid Thread id\n");
115 exit(0);
116 }
117
118 printf("\n");
119
120 switch(choice) {
121 case 1:
122 pthread_cancel(slaves[thread_num]);
123 break;
124 default:
125 continue;
126 }
127 }
128 return 0;
129}
Sagar, A. (2022). Part A - Multithreading & Thread Synchronization - Pthreads [Video file]. Retrieved from https://www.udemy.com/course/multithreading_parta/