thr_cancel.c revision 115278
153812Salfred/*
253812Salfred * David Leonard <d@openbsd.org>, 1999. Public domain.
353812Salfred * $FreeBSD: head/lib/libkse/thread/thr_cancel.c 115278 2003-05-24 02:29:25Z 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();
22115278Sdeischen	struct pthread *joinee = NULL;
2353812Salfred	int ret;
2453812Salfred
25113658Sdeischen	if ((ret = _thr_ref_add(curthread, pthread, /*include dead*/0)) == 0) {
26113658Sdeischen		/*
27113658Sdeischen		 * Take the scheduling lock while we change the cancel flags.
28113658Sdeischen		 */
29113658Sdeischen		THR_SCHED_LOCK(curthread, pthread);
3053812Salfred
3154708Sdeischen		if (((pthread->cancelflags & PTHREAD_CANCEL_DISABLE) != 0) ||
32113658Sdeischen		    (((pthread->cancelflags & THR_AT_CANCEL_POINT) == 0) &&
33113658Sdeischen		    ((pthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) == 0)))
3454708Sdeischen			/* Just mark it for cancellation: */
35113658Sdeischen			pthread->cancelflags |= THR_CANCELLING;
3654708Sdeischen		else {
3754708Sdeischen			/*
3854708Sdeischen			 * Check if we need to kick it back into the
3954708Sdeischen			 * run queue:
4054708Sdeischen			 */
4153812Salfred			switch (pthread->state) {
4253812Salfred			case PS_RUNNING:
4353812Salfred				/* No need to resume: */
44113658Sdeischen				pthread->cancelflags |= THR_CANCELLING;
4553812Salfred				break;
4653812Salfred
47113658Sdeischen			case PS_LOCKWAIT:
48113658Sdeischen				/*
49113658Sdeischen				 * These can't be removed from the queue.
50113658Sdeischen				 * Just mark it as cancelling and tell it
51113658Sdeischen				 * to yield once it leaves the critical
52113658Sdeischen				 * region.
53113658Sdeischen				 */
54113658Sdeischen				pthread->cancelflags |= THR_CANCELLING;
55113658Sdeischen				pthread->critical_yield = 1;
56113658Sdeischen				break;
57113658Sdeischen
5853812Salfred			case PS_SLEEP_WAIT:
59111035Smini			case PS_SIGSUSPEND:
60111035Smini			case PS_SIGWAIT:
6153812Salfred				/* Interrupt and resume: */
6253812Salfred				pthread->interrupted = 1;
63113658Sdeischen				pthread->cancelflags |= THR_CANCELLING;
64113658Sdeischen				_thr_setrunnable_unlocked(pthread);
6553812Salfred				break;
6653812Salfred
6776909Sjasone			case PS_JOIN:
68115278Sdeischen				/* Disconnect the thread from the joinee: */
69115278Sdeischen				joinee = pthread->join_status.thread;
70115278Sdeischen				pthread->join_status.thread = NULL;
71113658Sdeischen				pthread->cancelflags |= THR_CANCELLING;
72115278Sdeischen				_thr_setrunnable_unlocked(pthread);
73115278Sdeischen				if ((joinee != NULL) &&
74115278Sdeischen				    (curthread->kseg == joinee->kseg)) {
75115278Sdeischen					/* Remove the joiner from the joinee. */
76115278Sdeischen					joinee->joiner = NULL;
77115278Sdeischen					joinee = NULL;
78115278Sdeischen				}
7981750Sjasone				break;
8081750Sjasone
8161681Sjasone			case PS_SUSPENDED:
8253812Salfred			case PS_MUTEX_WAIT:
8353812Salfred			case PS_COND_WAIT:
8453812Salfred				/*
8553812Salfred				 * Threads in these states may be in queues.
8653812Salfred				 * In order to preserve queue integrity, the
8753812Salfred				 * cancelled thread must remove itself from the
8853812Salfred				 * queue.  Mark the thread as interrupted and
8953812Salfred				 * needing cancellation, and set the state to
9053812Salfred				 * running.  When the thread resumes, it will
9156277Sjasone				 * remove itself from the queue and call the
9256277Sjasone				 * cancellation completion routine.
9353812Salfred				 */
9453812Salfred				pthread->interrupted = 1;
95113658Sdeischen				pthread->cancelflags |= THR_CANCEL_NEEDED;
96113658Sdeischen				_thr_setrunnable_unlocked(pthread);
9756277Sjasone				pthread->continuation = finish_cancellation;
9853812Salfred				break;
9953812Salfred
10053812Salfred			case PS_DEAD:
10153812Salfred			case PS_DEADLOCK:
10253812Salfred			case PS_STATE_MAX:
10353812Salfred				/* Ignore - only here to silence -Wall: */
10453812Salfred				break;
10554708Sdeischen			}
106113658Sdeischen			if ((pthread->blocked != 0) &&
107113658Sdeischen			    ((pthread->cancelflags & THR_AT_CANCEL_POINT) != 0))
108113658Sdeischen				kse_thr_interrupt(&pthread->tmbx);
10953812Salfred		}
11054708Sdeischen
111113658Sdeischen		/*
112113658Sdeischen		 * Release the thread's scheduling lock and remove the
113113658Sdeischen		 * reference:
114113658Sdeischen		 */
115113658Sdeischen		THR_SCHED_UNLOCK(curthread, pthread);
116113658Sdeischen		_thr_ref_delete(curthread, pthread);
117115278Sdeischen
118115278Sdeischen		if ((joinee != NULL) &&
119115278Sdeischen		    (_thr_ref_add(curthread, joinee, /* include dead */1) == 0)) {
120115278Sdeischen			/* Remove the joiner from the joinee. */
121115278Sdeischen			THR_SCHED_LOCK(curthread, joinee);
122115278Sdeischen			joinee->joiner = NULL;
123115278Sdeischen			THR_SCHED_UNLOCK(curthread, joinee);
124115278Sdeischen			_thr_ref_delete(curthread, joinee);
125115278Sdeischen		}
12653812Salfred	}
12753812Salfred	return (ret);
12853812Salfred}
12953812Salfred
13053812Salfredint
13171581Sdeischen_pthread_setcancelstate(int state, int *oldstate)
13253812Salfred{
13371581Sdeischen	struct pthread	*curthread = _get_curthread();
13453812Salfred	int ostate;
13553812Salfred	int ret;
136113658Sdeischen	int need_exit = 0;
13753812Salfred
138113658Sdeischen	/* Take the scheduling lock while fiddling with the thread's state: */
139113658Sdeischen	THR_SCHED_LOCK(curthread, curthread);
140113658Sdeischen
14171581Sdeischen	ostate = curthread->cancelflags & PTHREAD_CANCEL_DISABLE;
14253812Salfred
14353812Salfred	switch (state) {
14453812Salfred	case PTHREAD_CANCEL_ENABLE:
14553812Salfred		if (oldstate != NULL)
14653812Salfred			*oldstate = ostate;
14771581Sdeischen		curthread->cancelflags &= ~PTHREAD_CANCEL_DISABLE;
14871581Sdeischen		if ((curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0)
149113658Sdeischen			need_exit = checkcancel(curthread);
15053812Salfred		ret = 0;
15153812Salfred		break;
15253812Salfred	case PTHREAD_CANCEL_DISABLE:
15353812Salfred		if (oldstate != NULL)
15453812Salfred			*oldstate = ostate;
15571581Sdeischen		curthread->cancelflags |= PTHREAD_CANCEL_DISABLE;
15653812Salfred		ret = 0;
15753812Salfred		break;
15853812Salfred	default:
15953812Salfred		ret = EINVAL;
16053812Salfred	}
16153812Salfred
162113658Sdeischen	THR_SCHED_UNLOCK(curthread, curthread);
163113658Sdeischen	if (need_exit != 0) {
164113658Sdeischen		_thr_exit_cleanup();
165113658Sdeischen		pthread_exit(PTHREAD_CANCELED);
166113658Sdeischen		PANIC("cancel");
167113658Sdeischen	}
16853812Salfred	return (ret);
16953812Salfred}
17053812Salfred
17153812Salfredint
17271581Sdeischen_pthread_setcanceltype(int type, int *oldtype)
17353812Salfred{
17471581Sdeischen	struct pthread	*curthread = _get_curthread();
17553812Salfred	int otype;
17653812Salfred	int ret;
177113658Sdeischen	int need_exit = 0;
17853812Salfred
179113658Sdeischen	/* Take the scheduling lock while fiddling with the state: */
180113658Sdeischen	THR_SCHED_LOCK(curthread, curthread);
181113658Sdeischen
18271581Sdeischen	otype = curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS;
18353812Salfred	switch (type) {
18453812Salfred	case PTHREAD_CANCEL_ASYNCHRONOUS:
18553812Salfred		if (oldtype != NULL)
18653812Salfred			*oldtype = otype;
18771581Sdeischen		curthread->cancelflags |= PTHREAD_CANCEL_ASYNCHRONOUS;
188113658Sdeischen		need_exit = checkcancel(curthread);
18953812Salfred		ret = 0;
19053812Salfred		break;
19153812Salfred	case PTHREAD_CANCEL_DEFERRED:
19253812Salfred		if (oldtype != NULL)
19353812Salfred			*oldtype = otype;
19471581Sdeischen		curthread->cancelflags &= ~PTHREAD_CANCEL_ASYNCHRONOUS;
19553812Salfred		ret = 0;
19653812Salfred		break;
19753812Salfred	default:
19853812Salfred		ret = EINVAL;
19953812Salfred	}
20053812Salfred
201113658Sdeischen	THR_SCHED_UNLOCK(curthread, curthread);
202113658Sdeischen	if (need_exit != 0) {
203113658Sdeischen		_thr_exit_cleanup();
204113658Sdeischen		pthread_exit(PTHREAD_CANCELED);
205113658Sdeischen		PANIC("cancel");
206113658Sdeischen	}
20753812Salfred	return (ret);
20853812Salfred}
20953812Salfred
210113658Sdeischenstatic int
211113658Sdeischencheckcancel(struct pthread *curthread)
21253812Salfred{
21371581Sdeischen	if (((curthread->cancelflags & PTHREAD_CANCEL_DISABLE) == 0) &&
214113658Sdeischen	    ((curthread->cancelflags & THR_CANCELLING) != 0)) {
21553812Salfred		/*
21653812Salfred		 * It is possible for this thread to be swapped out
21753812Salfred		 * while performing cancellation; do not allow it
21853812Salfred		 * to be cancelled again.
21953812Salfred		 */
220113658Sdeischen		curthread->cancelflags &= ~THR_CANCELLING;
221113658Sdeischen		return (1);
222113658Sdeischen	}
223113658Sdeischen	else
224113658Sdeischen		return (0);
225113658Sdeischen}
226113658Sdeischen
227113658Sdeischenstatic void
228113658Sdeischentestcancel(struct pthread *curthread)
229113658Sdeischen{
230113658Sdeischen	/* Take the scheduling lock while fiddling with the state: */
231113658Sdeischen
232113658Sdeischen	if (checkcancel(curthread) != 0) {
233113658Sdeischen		/* Unlock before exiting: */
234113658Sdeischen		THR_SCHED_UNLOCK(curthread, curthread);
235113658Sdeischen
236113658Sdeischen		_thr_exit_cleanup();
23753812Salfred		pthread_exit(PTHREAD_CANCELED);
23853812Salfred		PANIC("cancel");
23953812Salfred	}
24053812Salfred}
24153812Salfred
24253812Salfredvoid
243113658Sdeischen_pthread_testcancel(void)
24453812Salfred{
24571581Sdeischen	struct pthread	*curthread = _get_curthread();
24671581Sdeischen
247114187Sdeischen	THR_SCHED_LOCK(curthread, curthread);
248113658Sdeischen	testcancel(curthread);
249114187Sdeischen	THR_SCHED_UNLOCK(curthread, curthread);
250113658Sdeischen}
251113658Sdeischen
252113658Sdeischenvoid
253113658Sdeischen_thr_enter_cancellation_point(struct pthread *thread)
254113658Sdeischen{
25553812Salfred	/* Look for a cancellation before we block: */
256114187Sdeischen	THR_SCHED_LOCK(thread, thread);
257113658Sdeischen	testcancel(thread);
258113658Sdeischen	thread->cancelflags |= THR_AT_CANCEL_POINT;
259114187Sdeischen	THR_SCHED_UNLOCK(thread, thread);
26053812Salfred}
26153812Salfred
26253812Salfredvoid
263113658Sdeischen_thr_leave_cancellation_point(struct pthread *thread)
26453812Salfred{
265114187Sdeischen	THR_SCHED_LOCK(thread, thread);
266113658Sdeischen	thread->cancelflags &= ~THR_AT_CANCEL_POINT;
26753812Salfred	/* Look for a cancellation after we unblock: */
268113658Sdeischen	testcancel(thread);
269114187Sdeischen	THR_SCHED_UNLOCK(thread, thread);
27053812Salfred}
27156277Sjasone
27256277Sjasonestatic void
27356277Sjasonefinish_cancellation(void *arg)
27456277Sjasone{
27571581Sdeischen	struct pthread	*curthread = _get_curthread();
27656277Sjasone
27771581Sdeischen	curthread->continuation = NULL;
27871581Sdeischen	curthread->interrupted = 0;
27971581Sdeischen
280114187Sdeischen	THR_SCHED_LOCK(curthread, curthread);
281113658Sdeischen	if ((curthread->cancelflags & THR_CANCEL_NEEDED) != 0) {
282113658Sdeischen		curthread->cancelflags &= ~THR_CANCEL_NEEDED;
283114187Sdeischen		THR_SCHED_UNLOCK(curthread, curthread);
284113658Sdeischen		_thr_exit_cleanup();
28556277Sjasone		pthread_exit(PTHREAD_CANCELED);
28656277Sjasone	}
287114187Sdeischen	THR_SCHED_UNLOCK(curthread, curthread);
28856277Sjasone}
289