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