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