thr_cancel.c revision 71581
153812Salfred/*
253812Salfred * David Leonard <d@openbsd.org>, 1999. Public domain.
353812Salfred * $FreeBSD: head/lib/libkse/thread/thr_cancel.c 71581 2001-01-24 13:03:38Z deischen $
453812Salfred */
553812Salfred#include <sys/errno.h>
653812Salfred#include <pthread.h>
753812Salfred#include "pthread_private.h"
853812Salfred
956277Sjasonestatic void	finish_cancellation(void *arg);
1056277Sjasone
1171581Sdeischen#pragma weak	pthread_cancel=_pthread_cancel
1271581Sdeischen#pragma weak	pthread_setcancelstate=_pthread_setcancelstate
1371581Sdeischen#pragma weak	pthread_setcanceltype=_pthread_setcanceltype
1471581Sdeischen#pragma weak	pthread_testcancel=_pthread_testcancel
1571581Sdeischen
1653812Salfredint
1771581Sdeischen_pthread_cancel(pthread_t pthread)
1853812Salfred{
1953812Salfred	int ret;
2053812Salfred
2153812Salfred	if ((ret = _find_thread(pthread)) != 0) {
2253812Salfred		/* NOTHING */
2353812Salfred	} else if (pthread->state == PS_DEAD || pthread->state == PS_DEADLOCK) {
2453812Salfred		ret = 0;
2553812Salfred	} else {
2653812Salfred		/* Protect the scheduling queues: */
2753812Salfred		_thread_kern_sig_defer();
2853812Salfred
2954708Sdeischen		if (((pthread->cancelflags & PTHREAD_CANCEL_DISABLE) != 0) ||
3054708Sdeischen		    (((pthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) == 0) &&
3154708Sdeischen		    ((pthread->cancelflags & PTHREAD_AT_CANCEL_POINT) == 0)))
3254708Sdeischen			/* Just mark it for cancellation: */
3354708Sdeischen			pthread->cancelflags |= PTHREAD_CANCELLING;
3454708Sdeischen		else {
3554708Sdeischen			/*
3654708Sdeischen			 * Check if we need to kick it back into the
3754708Sdeischen			 * run queue:
3854708Sdeischen			 */
3953812Salfred			switch (pthread->state) {
4053812Salfred			case PS_RUNNING:
4153812Salfred				/* No need to resume: */
4253812Salfred				pthread->cancelflags |= PTHREAD_CANCELLING;
4353812Salfred				break;
4453812Salfred
4553812Salfred			case PS_SPINBLOCK:
4653812Salfred			case PS_FDR_WAIT:
4753812Salfred			case PS_FDW_WAIT:
4853812Salfred			case PS_POLL_WAIT:
4953812Salfred			case PS_SELECT_WAIT:
5054708Sdeischen				/* Remove these threads from the work queue: */
5153812Salfred				if ((pthread->flags & PTHREAD_FLAGS_IN_WORKQ)
5253812Salfred				    != 0)
5353812Salfred					PTHREAD_WORKQ_REMOVE(pthread);
5453812Salfred				/* Fall through: */
5553812Salfred			case PS_SIGTHREAD:
5653812Salfred			case PS_SLEEP_WAIT:
5753812Salfred			case PS_WAIT_WAIT:
5853812Salfred			case PS_SIGSUSPEND:
5953812Salfred			case PS_SIGWAIT:
6053812Salfred				/* Interrupt and resume: */
6153812Salfred				pthread->interrupted = 1;
6253812Salfred				pthread->cancelflags |= PTHREAD_CANCELLING;
6353812Salfred				PTHREAD_NEW_STATE(pthread,PS_RUNNING);
6453812Salfred				break;
6553812Salfred
6661681Sjasone			case PS_SUSPENDED:
6761681Sjasone				if (pthread->suspended == SUSP_NO ||
6861681Sjasone				    pthread->suspended == SUSP_YES ||
6961681Sjasone				    pthread->suspended == SUSP_NOWAIT) {
7061681Sjasone					/*
7161681Sjasone					 * This thread isn't in any scheduling
7261681Sjasone					 * queues; just change it's state:
7361681Sjasone					 */
7461681Sjasone					pthread->cancelflags |=
7561681Sjasone					    PTHREAD_CANCELLING;
7661681Sjasone					PTHREAD_SET_STATE(pthread, PS_RUNNING);
7761681Sjasone					break;
7861681Sjasone				}
7961681Sjasone				/* FALLTHROUGH */
8053812Salfred			case PS_MUTEX_WAIT:
8153812Salfred			case PS_COND_WAIT:
8253812Salfred			case PS_FDLR_WAIT:
8353812Salfred			case PS_FDLW_WAIT:
8453812Salfred			case PS_FILE_WAIT:
8553812Salfred			case PS_JOIN:
8653812Salfred				/*
8753812Salfred				 * Threads in these states may be in queues.
8853812Salfred				 * In order to preserve queue integrity, the
8953812Salfred				 * cancelled thread must remove itself from the
9053812Salfred				 * queue.  Mark the thread as interrupted and
9153812Salfred				 * needing cancellation, and set the state to
9253812Salfred				 * running.  When the thread resumes, it will
9356277Sjasone				 * remove itself from the queue and call the
9456277Sjasone				 * cancellation completion routine.
9553812Salfred				 */
9653812Salfred				pthread->interrupted = 1;
9753812Salfred				pthread->cancelflags |= PTHREAD_CANCEL_NEEDED;
9853812Salfred				PTHREAD_NEW_STATE(pthread,PS_RUNNING);
9956277Sjasone				pthread->continuation = finish_cancellation;
10053812Salfred				break;
10153812Salfred
10253812Salfred			case PS_DEAD:
10353812Salfred			case PS_DEADLOCK:
10453812Salfred			case PS_STATE_MAX:
10553812Salfred				/* Ignore - only here to silence -Wall: */
10653812Salfred				break;
10754708Sdeischen			}
10853812Salfred		}
10954708Sdeischen
11053812Salfred		/* Unprotect the scheduling queues: */
11153812Salfred		_thread_kern_sig_undefer();
11253812Salfred
11353812Salfred		ret = 0;
11453812Salfred	}
11553812Salfred	return (ret);
11653812Salfred}
11753812Salfred
11853812Salfredint
11971581Sdeischen_pthread_setcancelstate(int state, int *oldstate)
12053812Salfred{
12171581Sdeischen	struct pthread	*curthread = _get_curthread();
12253812Salfred	int ostate;
12353812Salfred	int ret;
12453812Salfred
12571581Sdeischen	ostate = curthread->cancelflags & PTHREAD_CANCEL_DISABLE;
12653812Salfred
12753812Salfred	switch (state) {
12853812Salfred	case PTHREAD_CANCEL_ENABLE:
12953812Salfred		if (oldstate != NULL)
13053812Salfred			*oldstate = ostate;
13171581Sdeischen		curthread->cancelflags &= ~PTHREAD_CANCEL_DISABLE;
13271581Sdeischen		if ((curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0)
13353812Salfred			pthread_testcancel();
13453812Salfred		ret = 0;
13553812Salfred		break;
13653812Salfred	case PTHREAD_CANCEL_DISABLE:
13753812Salfred		if (oldstate != NULL)
13853812Salfred			*oldstate = ostate;
13971581Sdeischen		curthread->cancelflags |= PTHREAD_CANCEL_DISABLE;
14053812Salfred		ret = 0;
14153812Salfred		break;
14253812Salfred	default:
14353812Salfred		ret = EINVAL;
14453812Salfred	}
14553812Salfred
14653812Salfred	return (ret);
14753812Salfred}
14853812Salfred
14953812Salfredint
15071581Sdeischen_pthread_setcanceltype(int type, int *oldtype)
15153812Salfred{
15271581Sdeischen	struct pthread	*curthread = _get_curthread();
15353812Salfred	int otype;
15453812Salfred	int ret;
15553812Salfred
15671581Sdeischen	otype = curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS;
15753812Salfred	switch (type) {
15853812Salfred	case PTHREAD_CANCEL_ASYNCHRONOUS:
15953812Salfred		if (oldtype != NULL)
16053812Salfred			*oldtype = otype;
16171581Sdeischen		curthread->cancelflags |= PTHREAD_CANCEL_ASYNCHRONOUS;
16253812Salfred		pthread_testcancel();
16353812Salfred		ret = 0;
16453812Salfred		break;
16553812Salfred	case PTHREAD_CANCEL_DEFERRED:
16653812Salfred		if (oldtype != NULL)
16753812Salfred			*oldtype = otype;
16871581Sdeischen		curthread->cancelflags &= ~PTHREAD_CANCEL_ASYNCHRONOUS;
16953812Salfred		ret = 0;
17053812Salfred		break;
17153812Salfred	default:
17253812Salfred		ret = EINVAL;
17353812Salfred	}
17453812Salfred
17553812Salfred	return (ret);
17653812Salfred}
17753812Salfred
17853812Salfredvoid
17971581Sdeischen_pthread_testcancel(void)
18053812Salfred{
18171581Sdeischen	struct pthread	*curthread = _get_curthread();
18271581Sdeischen
18371581Sdeischen	if (((curthread->cancelflags & PTHREAD_CANCEL_DISABLE) == 0) &&
18471581Sdeischen	    ((curthread->cancelflags & PTHREAD_CANCELLING) != 0)) {
18553812Salfred		/*
18653812Salfred		 * It is possible for this thread to be swapped out
18753812Salfred		 * while performing cancellation; do not allow it
18853812Salfred		 * to be cancelled again.
18953812Salfred		 */
19071581Sdeischen		curthread->cancelflags &= ~PTHREAD_CANCELLING;
19153812Salfred		_thread_exit_cleanup();
19253812Salfred		pthread_exit(PTHREAD_CANCELED);
19353812Salfred		PANIC("cancel");
19453812Salfred	}
19553812Salfred}
19653812Salfred
19753812Salfredvoid
19853812Salfred_thread_enter_cancellation_point(void)
19953812Salfred{
20071581Sdeischen	struct pthread	*curthread = _get_curthread();
20171581Sdeischen
20253812Salfred	/* Look for a cancellation before we block: */
20353812Salfred	pthread_testcancel();
20471581Sdeischen	curthread->cancelflags |= PTHREAD_AT_CANCEL_POINT;
20553812Salfred}
20653812Salfred
20753812Salfredvoid
20853812Salfred_thread_leave_cancellation_point(void)
20953812Salfred{
21071581Sdeischen	struct pthread	*curthread = _get_curthread();
21171581Sdeischen
21271581Sdeischen	curthread->cancelflags &= ~PTHREAD_AT_CANCEL_POINT;
21353812Salfred	/* Look for a cancellation after we unblock: */
21453812Salfred	pthread_testcancel();
21553812Salfred}
21656277Sjasone
21756277Sjasonestatic void
21856277Sjasonefinish_cancellation(void *arg)
21956277Sjasone{
22071581Sdeischen	struct pthread	*curthread = _get_curthread();
22156277Sjasone
22271581Sdeischen	curthread->continuation = NULL;
22371581Sdeischen	curthread->interrupted = 0;
22471581Sdeischen
22571581Sdeischen	if ((curthread->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) {
22671581Sdeischen		curthread->cancelflags &= ~PTHREAD_CANCEL_NEEDED;
22756277Sjasone		_thread_exit_cleanup();
22856277Sjasone		pthread_exit(PTHREAD_CANCELED);
22956277Sjasone	}
23056277Sjasone}
231