thr_cancel.c revision 114187
153812Salfred/*
253812Salfred * David Leonard <d@openbsd.org>, 1999. Public domain.
353812Salfred * $FreeBSD: head/lib/libkse/thread/thr_cancel.c 114187 2003-04-28 23:56:12Z deischen $
453812Salfred */
553812Salfred#include <sys/errno.h>
653812Salfred#include <pthread.h>
7103388Smini#include "thr_private.h"
853812Salfred
975369Sdeischen__weak_reference(_pthread_cancel, pthread_cancel);
1075369Sdeischen__weak_reference(_pthread_setcancelstate, pthread_setcancelstate);
1175369Sdeischen__weak_reference(_pthread_setcanceltype, pthread_setcanceltype);
1275369Sdeischen__weak_reference(_pthread_testcancel, pthread_testcancel);
1371581Sdeischen
14113658Sdeischenstatic int	checkcancel(struct pthread *curthread);
15113658Sdeischenstatic void	testcancel(struct pthread *curthread);
16113658Sdeischenstatic void	finish_cancellation(void *arg);
17113658Sdeischen
1853812Salfredint
1971581Sdeischen_pthread_cancel(pthread_t pthread)
2053812Salfred{
21113658Sdeischen	struct pthread *curthread = _get_curthread();
2253812Salfred	int ret;
2353812Salfred
24113658Sdeischen	if ((ret = _thr_ref_add(curthread, pthread, /*include dead*/0)) == 0) {
25113658Sdeischen		/*
26113658Sdeischen		 * Take the scheduling lock while we change the cancel flags.
27113658Sdeischen		 */
28113658Sdeischen		THR_SCHED_LOCK(curthread, pthread);
2953812Salfred
3054708Sdeischen		if (((pthread->cancelflags & PTHREAD_CANCEL_DISABLE) != 0) ||
31113658Sdeischen		    (((pthread->cancelflags & THR_AT_CANCEL_POINT) == 0) &&
32113658Sdeischen		    ((pthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) == 0)))
3354708Sdeischen			/* Just mark it for cancellation: */
34113658Sdeischen			pthread->cancelflags |= THR_CANCELLING;
3554708Sdeischen		else {
3654708Sdeischen			/*
3754708Sdeischen			 * Check if we need to kick it back into the
3854708Sdeischen			 * run queue:
3954708Sdeischen			 */
4053812Salfred			switch (pthread->state) {
4153812Salfred			case PS_RUNNING:
4253812Salfred				/* No need to resume: */
43113658Sdeischen				pthread->cancelflags |= THR_CANCELLING;
4453812Salfred				break;
4553812Salfred
46113658Sdeischen			case PS_LOCKWAIT:
47113658Sdeischen				/*
48113658Sdeischen				 * These can't be removed from the queue.
49113658Sdeischen				 * Just mark it as cancelling and tell it
50113658Sdeischen				 * to yield once it leaves the critical
51113658Sdeischen				 * region.
52113658Sdeischen				 */
53113658Sdeischen				pthread->cancelflags |= THR_CANCELLING;
54113658Sdeischen				pthread->critical_yield = 1;
55113658Sdeischen				break;
56113658Sdeischen
5753812Salfred			case PS_SLEEP_WAIT:
58111035Smini			case PS_SIGSUSPEND:
59111035Smini			case PS_SIGWAIT:
6053812Salfred				/* Interrupt and resume: */
6153812Salfred				pthread->interrupted = 1;
62113658Sdeischen				pthread->cancelflags |= THR_CANCELLING;
63113658Sdeischen				_thr_setrunnable_unlocked(pthread);
6453812Salfred				break;
6553812Salfred
6676909Sjasone			case PS_JOIN:
6781750Sjasone				/*
6888015Sdeischen				 * Disconnect the thread from the joinee:
6981750Sjasone				 */
7087988Sdeischen				if (pthread->join_status.thread != NULL) {
7187988Sdeischen					pthread->join_status.thread->joiner
7287988Sdeischen					    = NULL;
7391762Sdeischen					pthread->join_status.thread = NULL;
7481750Sjasone				}
75113658Sdeischen				pthread->cancelflags |= THR_CANCELLING;
76113658Sdeischen				_thr_setrunnable_unlocked(pthread);
7781750Sjasone				break;
7881750Sjasone
7961681Sjasone			case PS_SUSPENDED:
8053812Salfred			case PS_MUTEX_WAIT:
8153812Salfred			case PS_COND_WAIT:
8253812Salfred				/*
8353812Salfred				 * Threads in these states may be in queues.
8453812Salfred				 * In order to preserve queue integrity, the
8553812Salfred				 * cancelled thread must remove itself from the
8653812Salfred				 * queue.  Mark the thread as interrupted and
8753812Salfred				 * needing cancellation, and set the state to
8853812Salfred				 * running.  When the thread resumes, it will
8956277Sjasone				 * remove itself from the queue and call the
9056277Sjasone				 * cancellation completion routine.
9153812Salfred				 */
9253812Salfred				pthread->interrupted = 1;
93113658Sdeischen				pthread->cancelflags |= THR_CANCEL_NEEDED;
94113658Sdeischen				_thr_setrunnable_unlocked(pthread);
9556277Sjasone				pthread->continuation = finish_cancellation;
9653812Salfred				break;
9753812Salfred
9853812Salfred			case PS_DEAD:
9953812Salfred			case PS_DEADLOCK:
10053812Salfred			case PS_STATE_MAX:
10153812Salfred				/* Ignore - only here to silence -Wall: */
10253812Salfred				break;
10354708Sdeischen			}
104113658Sdeischen			if ((pthread->blocked != 0) &&
105113658Sdeischen			    ((pthread->cancelflags & THR_AT_CANCEL_POINT) != 0))
106113658Sdeischen				kse_thr_interrupt(&pthread->tmbx);
10753812Salfred		}
10854708Sdeischen
109113658Sdeischen		/*
110113658Sdeischen		 * Release the thread's scheduling lock and remove the
111113658Sdeischen		 * reference:
112113658Sdeischen		 */
113113658Sdeischen		THR_SCHED_UNLOCK(curthread, pthread);
114113658Sdeischen		_thr_ref_delete(curthread, pthread);
11553812Salfred	}
11653812Salfred	return (ret);
11753812Salfred}
11853812Salfred
11953812Salfredint
12071581Sdeischen_pthread_setcancelstate(int state, int *oldstate)
12153812Salfred{
12271581Sdeischen	struct pthread	*curthread = _get_curthread();
12353812Salfred	int ostate;
12453812Salfred	int ret;
125113658Sdeischen	int need_exit = 0;
12653812Salfred
127113658Sdeischen	/* Take the scheduling lock while fiddling with the thread's state: */
128113658Sdeischen	THR_SCHED_LOCK(curthread, curthread);
129113658Sdeischen
13071581Sdeischen	ostate = curthread->cancelflags & PTHREAD_CANCEL_DISABLE;
13153812Salfred
13253812Salfred	switch (state) {
13353812Salfred	case PTHREAD_CANCEL_ENABLE:
13453812Salfred		if (oldstate != NULL)
13553812Salfred			*oldstate = ostate;
13671581Sdeischen		curthread->cancelflags &= ~PTHREAD_CANCEL_DISABLE;
13771581Sdeischen		if ((curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0)
138113658Sdeischen			need_exit = checkcancel(curthread);
13953812Salfred		ret = 0;
14053812Salfred		break;
14153812Salfred	case PTHREAD_CANCEL_DISABLE:
14253812Salfred		if (oldstate != NULL)
14353812Salfred			*oldstate = ostate;
14471581Sdeischen		curthread->cancelflags |= PTHREAD_CANCEL_DISABLE;
14553812Salfred		ret = 0;
14653812Salfred		break;
14753812Salfred	default:
14853812Salfred		ret = EINVAL;
14953812Salfred	}
15053812Salfred
151113658Sdeischen	THR_SCHED_UNLOCK(curthread, curthread);
152113658Sdeischen	if (need_exit != 0) {
153113658Sdeischen		_thr_exit_cleanup();
154113658Sdeischen		pthread_exit(PTHREAD_CANCELED);
155113658Sdeischen		PANIC("cancel");
156113658Sdeischen	}
15753812Salfred	return (ret);
15853812Salfred}
15953812Salfred
16053812Salfredint
16171581Sdeischen_pthread_setcanceltype(int type, int *oldtype)
16253812Salfred{
16371581Sdeischen	struct pthread	*curthread = _get_curthread();
16453812Salfred	int otype;
16553812Salfred	int ret;
166113658Sdeischen	int need_exit = 0;
16753812Salfred
168113658Sdeischen	/* Take the scheduling lock while fiddling with the state: */
169113658Sdeischen	THR_SCHED_LOCK(curthread, curthread);
170113658Sdeischen
17171581Sdeischen	otype = curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS;
17253812Salfred	switch (type) {
17353812Salfred	case PTHREAD_CANCEL_ASYNCHRONOUS:
17453812Salfred		if (oldtype != NULL)
17553812Salfred			*oldtype = otype;
17671581Sdeischen		curthread->cancelflags |= PTHREAD_CANCEL_ASYNCHRONOUS;
177113658Sdeischen		need_exit = checkcancel(curthread);
17853812Salfred		ret = 0;
17953812Salfred		break;
18053812Salfred	case PTHREAD_CANCEL_DEFERRED:
18153812Salfred		if (oldtype != NULL)
18253812Salfred			*oldtype = otype;
18371581Sdeischen		curthread->cancelflags &= ~PTHREAD_CANCEL_ASYNCHRONOUS;
18453812Salfred		ret = 0;
18553812Salfred		break;
18653812Salfred	default:
18753812Salfred		ret = EINVAL;
18853812Salfred	}
18953812Salfred
190113658Sdeischen	THR_SCHED_UNLOCK(curthread, curthread);
191113658Sdeischen	if (need_exit != 0) {
192113658Sdeischen		_thr_exit_cleanup();
193113658Sdeischen		pthread_exit(PTHREAD_CANCELED);
194113658Sdeischen		PANIC("cancel");
195113658Sdeischen	}
19653812Salfred	return (ret);
19753812Salfred}
19853812Salfred
199113658Sdeischenstatic int
200113658Sdeischencheckcancel(struct pthread *curthread)
20153812Salfred{
20271581Sdeischen	if (((curthread->cancelflags & PTHREAD_CANCEL_DISABLE) == 0) &&
203113658Sdeischen	    ((curthread->cancelflags & THR_CANCELLING) != 0)) {
20453812Salfred		/*
20553812Salfred		 * It is possible for this thread to be swapped out
20653812Salfred		 * while performing cancellation; do not allow it
20753812Salfred		 * to be cancelled again.
20853812Salfred		 */
209113658Sdeischen		curthread->cancelflags &= ~THR_CANCELLING;
210113658Sdeischen		return (1);
211113658Sdeischen	}
212113658Sdeischen	else
213113658Sdeischen		return (0);
214113658Sdeischen}
215113658Sdeischen
216113658Sdeischenstatic void
217113658Sdeischentestcancel(struct pthread *curthread)
218113658Sdeischen{
219113658Sdeischen	/* Take the scheduling lock while fiddling with the state: */
220113658Sdeischen
221113658Sdeischen	if (checkcancel(curthread) != 0) {
222113658Sdeischen		/* Unlock before exiting: */
223113658Sdeischen		THR_SCHED_UNLOCK(curthread, curthread);
224113658Sdeischen
225113658Sdeischen		_thr_exit_cleanup();
22653812Salfred		pthread_exit(PTHREAD_CANCELED);
22753812Salfred		PANIC("cancel");
22853812Salfred	}
22953812Salfred}
23053812Salfred
23153812Salfredvoid
232113658Sdeischen_pthread_testcancel(void)
23353812Salfred{
23471581Sdeischen	struct pthread	*curthread = _get_curthread();
23571581Sdeischen
236114187Sdeischen	THR_SCHED_LOCK(curthread, curthread);
237113658Sdeischen	testcancel(curthread);
238114187Sdeischen	THR_SCHED_UNLOCK(curthread, curthread);
239113658Sdeischen}
240113658Sdeischen
241113658Sdeischenvoid
242113658Sdeischen_thr_enter_cancellation_point(struct pthread *thread)
243113658Sdeischen{
24453812Salfred	/* Look for a cancellation before we block: */
245114187Sdeischen	THR_SCHED_LOCK(thread, thread);
246113658Sdeischen	testcancel(thread);
247113658Sdeischen	thread->cancelflags |= THR_AT_CANCEL_POINT;
248114187Sdeischen	THR_SCHED_UNLOCK(thread, thread);
24953812Salfred}
25053812Salfred
25153812Salfredvoid
252113658Sdeischen_thr_leave_cancellation_point(struct pthread *thread)
25353812Salfred{
254114187Sdeischen	THR_SCHED_LOCK(thread, thread);
255113658Sdeischen	thread->cancelflags &= ~THR_AT_CANCEL_POINT;
25653812Salfred	/* Look for a cancellation after we unblock: */
257113658Sdeischen	testcancel(thread);
258114187Sdeischen	THR_SCHED_UNLOCK(thread, thread);
25953812Salfred}
26056277Sjasone
26156277Sjasonestatic void
26256277Sjasonefinish_cancellation(void *arg)
26356277Sjasone{
26471581Sdeischen	struct pthread	*curthread = _get_curthread();
26556277Sjasone
26671581Sdeischen	curthread->continuation = NULL;
26771581Sdeischen	curthread->interrupted = 0;
26871581Sdeischen
269114187Sdeischen	THR_SCHED_LOCK(curthread, curthread);
270113658Sdeischen	if ((curthread->cancelflags & THR_CANCEL_NEEDED) != 0) {
271113658Sdeischen		curthread->cancelflags &= ~THR_CANCEL_NEEDED;
272114187Sdeischen		THR_SCHED_UNLOCK(curthread, curthread);
273113658Sdeischen		_thr_exit_cleanup();
27456277Sjasone		pthread_exit(PTHREAD_CANCELED);
27556277Sjasone	}
276114187Sdeischen	THR_SCHED_UNLOCK(curthread, curthread);
27756277Sjasone}
278