1 /*
2 * David Leonard <d@openbsd.org>, 1999. Public domain.
3 * $FreeBSD: stable/9/lib/libkse/thread/thr_cancel.c 174689 2007-12-16 23:29:57Z deischen $
4 */
5 #include "namespace.h"
6 #include <sys/errno.h>
7 #include <pthread.h>
8 #include "un-namespace.h"
9 #include "thr_private.h"
10
11 __weak_reference(_pthread_cancel, pthread_cancel);
12 __weak_reference(_pthread_setcancelstate, pthread_setcancelstate);
13 __weak_reference(_pthread_setcanceltype, pthread_setcanceltype);
14 __weak_reference(_pthread_testcancel, pthread_testcancel);
15
16 static inline int
checkcancel(struct pthread * curthread)17 checkcancel(struct pthread *curthread)
18 {
19 if ((curthread->cancelflags & THR_CANCELLING) != 0) {
20 /*
21 * It is possible for this thread to be swapped out
22 * while performing cancellation; do not allow it
23 * to be cancelled again.
24 */
25 if ((curthread->flags & THR_FLAGS_EXITING) != 0) {
26 /*
27 * This may happen once, but after this, it
28 * shouldn't happen again.
29 */
30 curthread->cancelflags &= ~THR_CANCELLING;
31 return (0);
32 }
33 if ((curthread->cancelflags & PTHREAD_CANCEL_DISABLE) == 0) {
34 curthread->cancelflags &= ~THR_CANCELLING;
35 return (1);
36 }
37 }
38 return (0);
39 }
40
41 static inline void
testcancel(struct pthread * curthread)42 testcancel(struct pthread *curthread)
43 {
44 if (checkcancel(curthread) != 0) {
45 /* Unlock before exiting: */
46 THR_THREAD_UNLOCK(curthread, curthread);
47
48 _thr_exit_cleanup();
49 _pthread_exit(PTHREAD_CANCELED);
50 PANIC("cancel");
51 }
52 }
53
54 int
_pthread_cancel(pthread_t pthread)55 _pthread_cancel(pthread_t pthread)
56 {
57 struct pthread *curthread = _get_curthread();
58 struct pthread *joinee = NULL;
59 struct kse_mailbox *kmbx = NULL;
60 int ret;
61
62 if ((ret = _thr_ref_add(curthread, pthread, /*include dead*/0)) == 0) {
63 /*
64 * Take the thread's lock while we change the cancel flags.
65 */
66 THR_THREAD_LOCK(curthread, pthread);
67 THR_SCHED_LOCK(curthread, pthread);
68 if (pthread->flags & THR_FLAGS_EXITING) {
69 THR_SCHED_UNLOCK(curthread, pthread);
70 THR_THREAD_UNLOCK(curthread, pthread);
71 _thr_ref_delete(curthread, pthread);
72 return (ESRCH);
73 }
74 if (((pthread->cancelflags & PTHREAD_CANCEL_DISABLE) != 0) ||
75 (((pthread->cancelflags & THR_AT_CANCEL_POINT) == 0) &&
76 ((pthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) == 0)))
77 /* Just mark it for cancellation: */
78 pthread->cancelflags |= THR_CANCELLING;
79 else {
80 /*
81 * Check if we need to kick it back into the
82 * run queue:
83 */
84 switch (pthread->state) {
85 case PS_RUNNING:
86 /* No need to resume: */
87 pthread->cancelflags |= THR_CANCELLING;
88 break;
89
90 case PS_LOCKWAIT:
91 /*
92 * These can't be removed from the queue.
93 * Just mark it as cancelling and tell it
94 * to yield once it leaves the critical
95 * region.
96 */
97 pthread->cancelflags |= THR_CANCELLING;
98 pthread->critical_yield = 1;
99 break;
100
101 case PS_SLEEP_WAIT:
102 case PS_SIGSUSPEND:
103 case PS_SIGWAIT:
104 /* Interrupt and resume: */
105 pthread->interrupted = 1;
106 pthread->cancelflags |= THR_CANCELLING;
107 kmbx = _thr_setrunnable_unlocked(pthread);
108 break;
109
110 case PS_JOIN:
111 /* Disconnect the thread from the joinee: */
112 joinee = pthread->join_status.thread;
113 pthread->join_status.thread = NULL;
114 pthread->cancelflags |= THR_CANCELLING;
115 kmbx = _thr_setrunnable_unlocked(pthread);
116 if ((joinee != NULL) &&
117 (pthread->kseg == joinee->kseg)) {
118 /* Remove the joiner from the joinee. */
119 joinee->joiner = NULL;
120 joinee = NULL;
121 }
122 break;
123
124 case PS_SUSPENDED:
125 case PS_MUTEX_WAIT:
126 case PS_COND_WAIT:
127 /*
128 * Threads in these states may be in queues.
129 * In order to preserve queue integrity, the
130 * cancelled thread must remove itself from the
131 * queue. Mark the thread as interrupted and
132 * needing cancellation, and set the state to
133 * running. When the thread resumes, it will
134 * remove itself from the queue and call the
135 * cancellation completion routine.
136 */
137 pthread->interrupted = 1;
138 pthread->cancelflags |= THR_CANCEL_NEEDED;
139 kmbx = _thr_setrunnable_unlocked(pthread);
140 pthread->continuation =
141 _thr_finish_cancellation;
142 break;
143
144 case PS_DEAD:
145 case PS_DEADLOCK:
146 case PS_STATE_MAX:
147 /* Ignore - only here to silence -Wall: */
148 break;
149 }
150 if ((pthread->cancelflags & THR_AT_CANCEL_POINT) &&
151 (pthread->blocked != 0 ||
152 pthread->attr.flags & PTHREAD_SCOPE_SYSTEM))
153 kse_thr_interrupt(&pthread->tcb->tcb_tmbx,
154 KSE_INTR_INTERRUPT, 0);
155 }
156
157 /*
158 * Release the thread's lock and remove the
159 * reference:
160 */
161 THR_SCHED_UNLOCK(curthread, pthread);
162 THR_THREAD_UNLOCK(curthread, pthread);
163 _thr_ref_delete(curthread, pthread);
164 if (kmbx != NULL)
165 kse_wakeup(kmbx);
166
167 if ((joinee != NULL) &&
168 (_thr_ref_add(curthread, joinee, /* include dead */1) == 0)) {
169 /* Remove the joiner from the joinee. */
170 THR_SCHED_LOCK(curthread, joinee);
171 joinee->joiner = NULL;
172 THR_SCHED_UNLOCK(curthread, joinee);
173 _thr_ref_delete(curthread, joinee);
174 }
175 }
176 return (ret);
177 }
178
179 int
_pthread_setcancelstate(int state,int * oldstate)180 _pthread_setcancelstate(int state, int *oldstate)
181 {
182 struct pthread *curthread = _get_curthread();
183 int ostate;
184 int ret;
185 int need_exit = 0;
186
187 /* Take the thread's lock while fiddling with the state: */
188 THR_THREAD_LOCK(curthread, curthread);
189
190 ostate = curthread->cancelflags & PTHREAD_CANCEL_DISABLE;
191
192 switch (state) {
193 case PTHREAD_CANCEL_ENABLE:
194 curthread->cancelflags &= ~PTHREAD_CANCEL_DISABLE;
195 if ((curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0)
196 need_exit = checkcancel(curthread);
197 ret = 0;
198 break;
199 case PTHREAD_CANCEL_DISABLE:
200 curthread->cancelflags |= PTHREAD_CANCEL_DISABLE;
201 ret = 0;
202 break;
203 default:
204 ret = EINVAL;
205 }
206
207 THR_THREAD_UNLOCK(curthread, curthread);
208 if (need_exit != 0) {
209 _thr_exit_cleanup();
210 _pthread_exit(PTHREAD_CANCELED);
211 PANIC("cancel");
212 }
213 if (ret == 0 && oldstate != NULL)
214 *oldstate = ostate;
215
216 return (ret);
217 }
218
219 int
_pthread_setcanceltype(int type,int * oldtype)220 _pthread_setcanceltype(int type, int *oldtype)
221 {
222 struct pthread *curthread = _get_curthread();
223 int otype;
224 int ret;
225 int need_exit = 0;
226
227 /* Take the thread's lock while fiddling with the state: */
228 THR_THREAD_LOCK(curthread, curthread);
229
230 otype = curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS;
231 switch (type) {
232 case PTHREAD_CANCEL_ASYNCHRONOUS:
233 curthread->cancelflags |= PTHREAD_CANCEL_ASYNCHRONOUS;
234 need_exit = checkcancel(curthread);
235 ret = 0;
236 break;
237 case PTHREAD_CANCEL_DEFERRED:
238 curthread->cancelflags &= ~PTHREAD_CANCEL_ASYNCHRONOUS;
239 ret = 0;
240 break;
241 default:
242 ret = EINVAL;
243 }
244
245 THR_THREAD_UNLOCK(curthread, curthread);
246 if (need_exit != 0) {
247 _thr_exit_cleanup();
248 _pthread_exit(PTHREAD_CANCELED);
249 PANIC("cancel");
250 }
251 if (ret == 0 && oldtype != NULL)
252 *oldtype = otype;
253
254 return (ret);
255 }
256
257 void
_pthread_testcancel(void)258 _pthread_testcancel(void)
259 {
260 struct pthread *curthread = _get_curthread();
261
262 THR_THREAD_LOCK(curthread, curthread);
263 testcancel(curthread);
264 THR_THREAD_UNLOCK(curthread, curthread);
265 }
266
267 void
_thr_cancel_enter(struct pthread * thread)268 _thr_cancel_enter(struct pthread *thread)
269 {
270 /* Look for a cancellation before we block: */
271 THR_THREAD_LOCK(thread, thread);
272 testcancel(thread);
273 thread->cancelflags |= THR_AT_CANCEL_POINT;
274 THR_THREAD_UNLOCK(thread, thread);
275 }
276
277 void
_thr_cancel_leave(struct pthread * thread,int check)278 _thr_cancel_leave(struct pthread *thread, int check)
279 {
280 THR_THREAD_LOCK(thread, thread);
281 thread->cancelflags &= ~THR_AT_CANCEL_POINT;
282 /* Look for a cancellation after we unblock: */
283 if (check)
284 testcancel(thread);
285 THR_THREAD_UNLOCK(thread, thread);
286 }
287
288 void
_thr_finish_cancellation(void * arg __unused)289 _thr_finish_cancellation(void *arg __unused)
290 {
291 struct pthread *curthread = _get_curthread();
292
293 curthread->continuation = NULL;
294 curthread->interrupted = 0;
295
296 THR_THREAD_LOCK(curthread, curthread);
297 if ((curthread->cancelflags & THR_CANCEL_NEEDED) != 0) {
298 curthread->cancelflags &= ~THR_CANCEL_NEEDED;
299 THR_THREAD_UNLOCK(curthread, curthread);
300 _thr_exit_cleanup();
301 _pthread_exit(PTHREAD_CANCELED);
302 }
303 THR_THREAD_UNLOCK(curthread, curthread);
304 }
305