]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - lib/libkse/thread/thr_cancel.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / lib / libkse / thread / thr_cancel.c
1 /*
2  * David Leonard <d@openbsd.org>, 1999. Public domain.
3  * $FreeBSD$
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
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
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
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
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
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
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
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
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
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 }