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