xref: /freebsd/lib/libthr/thread/thr_cancel.c (revision 6b3455a7665208c366849f0b2b3bc916fb97516e)
1 /*
2  * David Leonard <d@openbsd.org>, 1999. Public domain.
3  * $FreeBSD$
4  */
5 #include <sys/errno.h>
6 #include <pthread.h>
7 #include <stdlib.h>
8 #include "thr_private.h"
9 
10 /*
11  * Static prototypes
12  */
13 static void	testcancel(void);
14 
15 __weak_reference(_pthread_cancel, pthread_cancel);
16 __weak_reference(_pthread_setcancelstate, pthread_setcancelstate);
17 __weak_reference(_pthread_setcanceltype, pthread_setcanceltype);
18 __weak_reference(_pthread_testcancel, pthread_testcancel);
19 
20 /*
21  * Posix requires this function to be async-cancel-safe, so it
22  * may not aquire any type of resource or call any functions
23  * that might do so.
24  */
25 int
26 _pthread_cancel(pthread_t pthread)
27 {
28 	/* Don't continue if cancellation has already been set. */
29 	if (atomic_cmpset_int(&pthread->cancellation, (int)CS_NULL,
30 	    (int)CS_PENDING) != 1)
31 		return (0);
32 
33 	/*
34 	 * Only wakeup threads that are in cancellation points or
35 	 * have set async cancel.
36 	 * XXX - access to pthread->flags is not safe. We should just
37 	 *	 unconditionally wake the thread and make sure that
38 	 *	 the the library correctly handles spurious wakeups.
39 	 */
40 	if ((pthread->cancellationpoint || pthread->cancelmode == M_ASYNC) &&
41 	    (pthread->flags & PTHREAD_FLAGS_NOT_RUNNING) != 0)
42 		PTHREAD_WAKE(pthread);
43 	return (0);
44 }
45 
46 /*
47  * Posix requires this function to be async-cancel-safe, so it
48  * may not aquire any type of resource or call any functions
49  * that might do so.
50  */
51 int
52 _pthread_setcancelstate(int state, int *oldstate)
53 {
54 	int ostate;
55 
56 	ostate = (curthread->cancelmode == M_OFF) ? PTHREAD_CANCEL_DISABLE :
57 	    PTHREAD_CANCEL_ENABLE;
58 	switch (state) {
59 	case PTHREAD_CANCEL_ENABLE:
60 		curthread->cancelmode = curthread->cancelstate;
61 		break;
62 	case PTHREAD_CANCEL_DISABLE:
63 		if (curthread->cancelmode != M_OFF) {
64 			curthread->cancelstate = curthread->cancelmode;
65 			curthread->cancelmode = M_OFF;
66 		}
67 		break;
68 	default:
69 		return (EINVAL);
70 	}
71 	if (oldstate != NULL)
72 		*oldstate = ostate;
73 	return (0);
74 }
75 
76 /*
77  * Posix requires this function to be async-cancel-safe, so it
78  * may not aquire any type of resource or call any functions that
79  * might do so.
80  */
81 int
82 _pthread_setcanceltype(int type, int *oldtype)
83 {
84 	enum cancel_mode omode;
85 
86 	omode = curthread->cancelstate;
87 	switch (type) {
88 	case PTHREAD_CANCEL_ASYNCHRONOUS:
89 		if (curthread->cancelmode != M_OFF)
90 			curthread->cancelmode = M_ASYNC;
91 		curthread->cancelstate = M_ASYNC;
92 		break;
93 	case PTHREAD_CANCEL_DEFERRED:
94 		if (curthread->cancelmode != M_OFF)
95 			curthread->cancelmode = M_DEFERRED;
96 		curthread->cancelstate = M_DEFERRED;
97 		break;
98 	default:
99 		return (EINVAL);
100 	}
101 	if (oldtype != NULL) {
102 		if (omode == M_DEFERRED)
103 			*oldtype = PTHREAD_CANCEL_DEFERRED;
104 		else if (omode == M_ASYNC)
105 			*oldtype = PTHREAD_CANCEL_ASYNCHRONOUS;
106 	}
107 	return (0);
108 }
109 
110 void
111 _pthread_testcancel(void)
112 {
113 	testcancel();
114 }
115 
116 static void
117 testcancel()
118 {
119 	if (curthread->cancelmode != M_OFF) {
120 
121 		/* Cleanup a canceled thread only once. */
122 		if (atomic_cmpset_int(&curthread->cancellation,
123 		    (int)CS_PENDING, (int)CS_SET) == 1) {
124 			_thread_exit_cleanup();
125 			pthread_exit(PTHREAD_CANCELED);
126 			PANIC("cancel");
127 		}
128 	}
129 }
130 
131 void
132 _thread_enter_cancellation_point(void)
133 {
134 	testcancel();
135 	curthread->cancellationpoint = 1;
136 }
137 
138 void
139 _thread_leave_cancellation_point(void)
140 {
141 	curthread->cancellationpoint = 0;
142 	testcancel();
143 }
144