thr_cancel.c revision 116972
1/*
2 * David Leonard <d@openbsd.org>, 1999. Public domain.
3 * $FreeBSD: head/lib/libkse/thread/thr_cancel.c 116972 2003-06-28 09:39:35Z 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				    (curthread->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		if (oldstate != NULL)
150			*oldstate = ostate;
151		curthread->cancelflags &= ~PTHREAD_CANCEL_DISABLE;
152		if ((curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0)
153			need_exit = checkcancel(curthread);
154		ret = 0;
155		break;
156	case PTHREAD_CANCEL_DISABLE:
157		if (oldstate != NULL)
158			*oldstate = ostate;
159		curthread->cancelflags |= PTHREAD_CANCEL_DISABLE;
160		ret = 0;
161		break;
162	default:
163		ret = EINVAL;
164	}
165
166	THR_SCHED_UNLOCK(curthread, curthread);
167	if (need_exit != 0) {
168		_thr_exit_cleanup();
169		pthread_exit(PTHREAD_CANCELED);
170		PANIC("cancel");
171	}
172	return (ret);
173}
174
175int
176_pthread_setcanceltype(int type, int *oldtype)
177{
178	struct pthread	*curthread = _get_curthread();
179	int otype;
180	int ret;
181	int need_exit = 0;
182
183	/* Take the scheduling lock while fiddling with the state: */
184	THR_SCHED_LOCK(curthread, curthread);
185
186	otype = curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS;
187	switch (type) {
188	case PTHREAD_CANCEL_ASYNCHRONOUS:
189		if (oldtype != NULL)
190			*oldtype = otype;
191		curthread->cancelflags |= PTHREAD_CANCEL_ASYNCHRONOUS;
192		need_exit = checkcancel(curthread);
193		ret = 0;
194		break;
195	case PTHREAD_CANCEL_DEFERRED:
196		if (oldtype != NULL)
197			*oldtype = otype;
198		curthread->cancelflags &= ~PTHREAD_CANCEL_ASYNCHRONOUS;
199		ret = 0;
200		break;
201	default:
202		ret = EINVAL;
203	}
204
205	THR_SCHED_UNLOCK(curthread, curthread);
206	if (need_exit != 0) {
207		_thr_exit_cleanup();
208		pthread_exit(PTHREAD_CANCELED);
209		PANIC("cancel");
210	}
211	return (ret);
212}
213
214static int
215checkcancel(struct pthread *curthread)
216{
217	if (((curthread->cancelflags & PTHREAD_CANCEL_DISABLE) == 0) &&
218	    ((curthread->cancelflags & THR_CANCELLING) != 0)) {
219		/*
220		 * It is possible for this thread to be swapped out
221		 * while performing cancellation; do not allow it
222		 * to be cancelled again.
223		 */
224		curthread->cancelflags &= ~THR_CANCELLING;
225		return (1);
226	}
227	else
228		return (0);
229}
230
231static void
232testcancel(struct pthread *curthread)
233{
234	/* Take the scheduling lock while fiddling with the state: */
235
236	if (checkcancel(curthread) != 0) {
237		/* Unlock before exiting: */
238		THR_SCHED_UNLOCK(curthread, curthread);
239
240		_thr_exit_cleanup();
241		pthread_exit(PTHREAD_CANCELED);
242		PANIC("cancel");
243	}
244}
245
246void
247_pthread_testcancel(void)
248{
249	struct pthread	*curthread = _get_curthread();
250
251	THR_SCHED_LOCK(curthread, curthread);
252	testcancel(curthread);
253	THR_SCHED_UNLOCK(curthread, curthread);
254}
255
256void
257_thr_enter_cancellation_point(struct pthread *thread)
258{
259	/* Look for a cancellation before we block: */
260	THR_SCHED_LOCK(thread, thread);
261	testcancel(thread);
262	thread->cancelflags |= THR_AT_CANCEL_POINT;
263	THR_SCHED_UNLOCK(thread, thread);
264}
265
266void
267_thr_leave_cancellation_point(struct pthread *thread)
268{
269	THR_SCHED_LOCK(thread, thread);
270	thread->cancelflags &= ~THR_AT_CANCEL_POINT;
271	/* Look for a cancellation after we unblock: */
272	testcancel(thread);
273	THR_SCHED_UNLOCK(thread, thread);
274}
275
276static void
277finish_cancellation(void *arg)
278{
279	struct pthread	*curthread = _get_curthread();
280
281	curthread->continuation = NULL;
282	curthread->interrupted = 0;
283
284	THR_SCHED_LOCK(curthread, curthread);
285	if ((curthread->cancelflags & THR_CANCEL_NEEDED) != 0) {
286		curthread->cancelflags &= ~THR_CANCEL_NEEDED;
287		THR_SCHED_UNLOCK(curthread, curthread);
288		_thr_exit_cleanup();
289		pthread_exit(PTHREAD_CANCELED);
290	}
291	THR_SCHED_UNLOCK(curthread, curthread);
292}
293