Deleted Added
full compact
thr_cancel.c (126000) thr_cancel.c (129484)
1/*
2 * David Leonard <d@openbsd.org>, 1999. Public domain.
1/*
2 * David Leonard <d@openbsd.org>, 1999. Public domain.
3 * $FreeBSD: head/lib/libthr/thread/thr_cancel.c 126000 2004-02-19 13:51:52Z mtm $
3 * $FreeBSD: head/lib/libthr/thread/thr_cancel.c 129484 2004-05-20 12:06:16Z mtm $
4 */
5#include <sys/errno.h>
6#include <pthread.h>
7#include <stdlib.h>
8#include "thr_private.h"
9
10/*
11 * Static prototypes
12 */
13static void testcancel(void);
14
15__weak_reference(_pthread_cancel, pthread_cancel);
16__weak_reference(_pthread_setcancelstate, pthread_setcancelstate);
17__weak_reference(_pthread_setcanceltype, pthread_setcanceltype);
18__weak_reference(_pthread_testcancel, pthread_testcancel);
19
4 */
5#include <sys/errno.h>
6#include <pthread.h>
7#include <stdlib.h>
8#include "thr_private.h"
9
10/*
11 * Static prototypes
12 */
13static void testcancel(void);
14
15__weak_reference(_pthread_cancel, pthread_cancel);
16__weak_reference(_pthread_setcancelstate, pthread_setcancelstate);
17__weak_reference(_pthread_setcanceltype, pthread_setcanceltype);
18__weak_reference(_pthread_testcancel, pthread_testcancel);
19
20/*
21 * Posix requires this function to be async-cancel-safe, so it
22 * may not aquire any type of resource or call any functions
23 * that might do so.
24 */
20int
21_pthread_cancel(pthread_t pthread)
22{
25int
26_pthread_cancel(pthread_t pthread)
27{
23 int ret;
24 pthread_t joined;
28 /* Don't continue if cancellation has already been set. */
29 if (atomic_cmpset_int(&pthread->cancellation, (int)CS_NULL,
30 (int)CS_PENDING) != 1)
31 return (0);
25
26 /*
32
33 /*
27 * When canceling a thread that has joined another thread, this
28 * routine breaks the normal lock order of locking first the
29 * joined and then the joiner. Therefore, it is necessary that
30 * if it can't obtain the second lock, that it release the first
31 * one and restart from the top.
34 * Only wakeup threads that are in cancellation points or
35 * have set async cancel.
36 * XXX - access to pthread->flags is not safe. We should just
37 * unconditionally wake the thread and make sure that
38 * the the library correctly handles spurious wakeups.
32 */
39 */
33retry:
34 if ((ret = _find_thread(pthread)) != 0)
35 /* The thread is not on the list of active threads */
36 goto out;
37
38 _thread_critical_enter(pthread);
39
40 if (pthread->state == PS_DEAD || pthread->state == PS_DEADLOCK
41 || (pthread->flags & PTHREAD_EXITING) != 0) {
42 /*
43 * The thread is in the process of (or has already) exited
44 * or is deadlocked.
45 */
46 _thread_critical_exit(pthread);
47 ret = 0;
48 goto out;
49 }
50
51 /*
52 * The thread is on the active thread list and is not in the process
53 * of exiting.
54 */
55
56 if (((pthread->cancelflags & PTHREAD_CANCEL_DISABLE) != 0) ||
57 (((pthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) == 0) &&
58 ((pthread->cancelflags & PTHREAD_AT_CANCEL_POINT) == 0)))
59 /* Just mark it for cancellation: */
60 pthread->cancelflags |= PTHREAD_CANCELLING;
61 else {
62 /*
63 * Check if we need to kick it back into the
64 * run queue:
65 */
66 switch (pthread->state) {
67 case PS_RUNNING:
68 /* No need to resume: */
69 pthread->cancelflags |= PTHREAD_CANCELLING;
70 break;
71
72 case PS_SLEEP_WAIT:
73 case PS_WAIT_WAIT:
74 pthread->cancelflags |= PTHREAD_CANCELLING;
75 PTHREAD_NEW_STATE(pthread, PS_RUNNING);
76 break;
77
78 case PS_JOIN:
79 /*
80 * Disconnect the thread from the joinee:
81 */
82 if ((joined = pthread->join_status.thread) != NULL) {
83 UMTX_TRYLOCK(&joined->lock, ret);
84 if (ret == EBUSY) {
85 _thread_critical_exit(pthread);
86 goto retry;
87 }
88 pthread->join_status.thread->joiner = NULL;
89 UMTX_UNLOCK(&joined->lock);
90 joined = pthread->join_status.thread = NULL;
91 }
92 pthread->cancelflags |= PTHREAD_CANCELLING;
93 PTHREAD_NEW_STATE(pthread, PS_RUNNING);
94 break;
95
96 case PS_BARRIER_WAIT:
97 case PS_MUTEX_WAIT:
98 case PS_COND_WAIT:
99 /*
100 * Threads in these states may be in queues.
101 * In order to preserve queue integrity, the
102 * cancelled thread must remove itself from the
103 * queue. When the thread resumes, it will
104 * remove itself from the queue and call the
105 * cancellation routine.
106 */
107 pthread->cancelflags |= PTHREAD_CANCELLING;
108 PTHREAD_NEW_STATE(pthread, PS_RUNNING);
109 break;
110
111 case PS_DEAD:
112 case PS_DEADLOCK:
113 case PS_STATE_MAX:
114 /* Ignore - only here to silence -Wall: */
115 break;
116 }
117 }
118
119 /* Unprotect the scheduling queues: */
120 _thread_critical_exit(pthread);
121
122 ret = 0;
123out:
124 return (ret);
40 if ((pthread->cancellationpoint || pthread->cancelmode == M_ASYNC) &&
41 (pthread->flags & PTHREAD_FLAGS_NOT_RUNNING) != 0)
42 PTHREAD_WAKE(pthread);
43 return (0);
125}
126
44}
45
46/*
47 * Posix requires this function to be async-cancel-safe, so it
48 * may not aquire any type of resource or call any functions
49 * that might do so.
50 */
127int
128_pthread_setcancelstate(int state, int *oldstate)
129{
51int
52_pthread_setcancelstate(int state, int *oldstate)
53{
130 int ostate, ret;
54 int ostate;
131
55
132 ret = 0;
133
134 _thread_critical_enter(curthread);
135
136 ostate = curthread->cancelflags & PTHREAD_CANCEL_DISABLE;
137
56 ostate = (curthread->cancelmode == M_OFF) ? PTHREAD_CANCEL_DISABLE :
57 PTHREAD_CANCEL_ENABLE;
138 switch (state) {
139 case PTHREAD_CANCEL_ENABLE:
58 switch (state) {
59 case PTHREAD_CANCEL_ENABLE:
140 if (oldstate != NULL)
141 *oldstate = ostate;
142 curthread->cancelflags &= ~PTHREAD_CANCEL_DISABLE;
143 if ((curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) == 0)
144 break;
145 testcancel();
60 curthread->cancelmode = curthread->cancelstate;
146 break;
147 case PTHREAD_CANCEL_DISABLE:
61 break;
62 case PTHREAD_CANCEL_DISABLE:
148 if (oldstate != NULL)
149 *oldstate = ostate;
150 curthread->cancelflags |= PTHREAD_CANCEL_DISABLE;
63 if (curthread->cancelmode != M_OFF) {
64 curthread->cancelstate = curthread->cancelmode;
65 curthread->cancelmode = M_OFF;
66 }
151 break;
152 default:
67 break;
68 default:
153 ret = EINVAL;
69 return (EINVAL);
154 }
70 }
155
156 _thread_critical_exit(curthread);
157 return (ret);
71 if (oldstate != NULL)
72 *oldstate = ostate;
73 return (0);
158}
159
74}
75
76/*
77 * Posix requires this function to be async-cancel-safe, so it
78 * may not aquire any type of resource or call any functions that
79 * might do so.
80 */
160int
161_pthread_setcanceltype(int type, int *oldtype)
162{
81int
82_pthread_setcanceltype(int type, int *oldtype)
83{
163 int otype;
84 enum cancel_mode omode;
164
85
165 _thread_critical_enter(curthread);
166 otype = curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS;
86 omode = curthread->cancelstate;
167 switch (type) {
168 case PTHREAD_CANCEL_ASYNCHRONOUS:
87 switch (type) {
88 case PTHREAD_CANCEL_ASYNCHRONOUS:
169 if (oldtype != NULL)
170 *oldtype = otype;
171 curthread->cancelflags |= PTHREAD_CANCEL_ASYNCHRONOUS;
172 testcancel();
89 if (curthread->cancelmode != M_OFF)
90 curthread->cancelmode = M_ASYNC;
91 curthread->cancelstate = M_ASYNC;
173 break;
174 case PTHREAD_CANCEL_DEFERRED:
92 break;
93 case PTHREAD_CANCEL_DEFERRED:
175 if (oldtype != NULL)
176 *oldtype = otype;
177 curthread->cancelflags &= ~PTHREAD_CANCEL_ASYNCHRONOUS;
94 if (curthread->cancelmode != M_OFF)
95 curthread->cancelmode = M_DEFERRED;
96 curthread->cancelstate = M_DEFERRED;
178 break;
179 default:
180 return (EINVAL);
181 }
97 break;
98 default:
99 return (EINVAL);
100 }
182
183 _thread_critical_exit(curthread);
101 if (oldtype != NULL) {
102 if (omode == M_DEFERRED)
103 *oldtype = PTHREAD_CANCEL_DEFERRED;
104 else if (omode == M_ASYNC)
105 *oldtype = PTHREAD_CANCEL_ASYNCHRONOUS;
106 }
184 return (0);
185}
186
187void
188_pthread_testcancel(void)
189{
107 return (0);
108}
109
110void
111_pthread_testcancel(void)
112{
190 _thread_critical_enter(curthread);
191 testcancel();
113 testcancel();
192 _thread_critical_exit(curthread);
193}
194
195static void
196testcancel()
197{
114}
115
116static void
117testcancel()
118{
198 /*
199 * This pthread should already be locked by the caller.
200 */
119 if (curthread->cancelmode != M_OFF) {
201
120
202 if (((curthread->cancelflags & PTHREAD_CANCEL_DISABLE) == 0) &&
203 ((curthread->cancelflags & PTHREAD_CANCELLING) != 0) &&
204 ((curthread->flags & PTHREAD_EXITING) == 0)) {
205 /*
206 * It is possible for this thread to be swapped out
207 * while performing cancellation; do not allow it
208 * to be cancelled again.
209 */
210 curthread->cancelflags &= ~PTHREAD_CANCELLING;
211 _thread_critical_exit(curthread);
212 _thread_exit_cleanup();
213 pthread_exit(PTHREAD_CANCELED);
214 PANIC("cancel");
121 /* Cleanup a canceled thread only once. */
122 if (atomic_cmpset_int(&curthread->cancellation,
123 (int)CS_PENDING, (int)CS_SET) == 1) {
124 _thread_exit_cleanup();
125 pthread_exit(PTHREAD_CANCELED);
126 PANIC("cancel");
127 }
215 }
216}
217
218void
219_thread_enter_cancellation_point(void)
220{
128 }
129}
130
131void
132_thread_enter_cancellation_point(void)
133{
221 _thread_critical_enter(curthread);
222 testcancel();
134 testcancel();
223 curthread->cancelflags |= PTHREAD_AT_CANCEL_POINT;
224 _thread_critical_exit(curthread);
135 curthread->cancellationpoint = 1;
225}
226
227void
228_thread_leave_cancellation_point(void)
229{
136}
137
138void
139_thread_leave_cancellation_point(void)
140{
230 _thread_critical_enter(curthread);
231 curthread->cancelflags &= ~PTHREAD_AT_CANCEL_POINT;
141 curthread->cancellationpoint = 0;
232 testcancel();
142 testcancel();
233 _thread_critical_exit(curthread);
234
235}
143}