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