thr_cancel.c revision 115278
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}
289