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