thr_cancel.c revision 91762
153812Salfred/*
253812Salfred * David Leonard <d@openbsd.org>, 1999. Public domain.
353812Salfred * $FreeBSD: head/lib/libkse/thread/thr_cancel.c 91762 2002-03-06 19:28:41Z deischen $
453812Salfred */
553812Salfred#include <sys/errno.h>
653812Salfred#include <pthread.h>
753812Salfred#include "pthread_private.h"
853812Salfred
956277Sjasonestatic void	finish_cancellation(void *arg);
1056277Sjasone
1175369Sdeischen__weak_reference(_pthread_cancel, pthread_cancel);
1275369Sdeischen__weak_reference(_pthread_setcancelstate, pthread_setcancelstate);
1375369Sdeischen__weak_reference(_pthread_setcanceltype, pthread_setcanceltype);
1475369Sdeischen__weak_reference(_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
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				}
7581750Sjasone				pthread->cancelflags |= PTHREAD_CANCELLING;
7681750Sjasone				PTHREAD_NEW_STATE(pthread, PS_RUNNING);
7781750Sjasone				break;
7881750Sjasone
7961681Sjasone			case PS_SUSPENDED:
8061681Sjasone				if (pthread->suspended == SUSP_NO ||
8161681Sjasone				    pthread->suspended == SUSP_YES ||
8281750Sjasone				    pthread->suspended == SUSP_JOIN ||
8361681Sjasone				    pthread->suspended == SUSP_NOWAIT) {
8461681Sjasone					/*
8561681Sjasone					 * This thread isn't in any scheduling
8661681Sjasone					 * queues; just change it's state:
8761681Sjasone					 */
8861681Sjasone					pthread->cancelflags |=
8961681Sjasone					    PTHREAD_CANCELLING;
9061681Sjasone					PTHREAD_SET_STATE(pthread, PS_RUNNING);
9161681Sjasone					break;
9261681Sjasone				}
9361681Sjasone				/* FALLTHROUGH */
9453812Salfred			case PS_MUTEX_WAIT:
9553812Salfred			case PS_COND_WAIT:
9653812Salfred			case PS_FDLR_WAIT:
9753812Salfred			case PS_FDLW_WAIT:
9853812Salfred			case PS_FILE_WAIT:
9953812Salfred				/*
10053812Salfred				 * Threads in these states may be in queues.
10153812Salfred				 * In order to preserve queue integrity, the
10253812Salfred				 * cancelled thread must remove itself from the
10353812Salfred				 * queue.  Mark the thread as interrupted and
10453812Salfred				 * needing cancellation, and set the state to
10553812Salfred				 * running.  When the thread resumes, it will
10656277Sjasone				 * remove itself from the queue and call the
10756277Sjasone				 * cancellation completion routine.
10853812Salfred				 */
10953812Salfred				pthread->interrupted = 1;
11053812Salfred				pthread->cancelflags |= PTHREAD_CANCEL_NEEDED;
11153812Salfred				PTHREAD_NEW_STATE(pthread,PS_RUNNING);
11256277Sjasone				pthread->continuation = finish_cancellation;
11353812Salfred				break;
11453812Salfred
11553812Salfred			case PS_DEAD:
11653812Salfred			case PS_DEADLOCK:
11753812Salfred			case PS_STATE_MAX:
11853812Salfred				/* Ignore - only here to silence -Wall: */
11953812Salfred				break;
12054708Sdeischen			}
12153812Salfred		}
12254708Sdeischen
12353812Salfred		/* Unprotect the scheduling queues: */
12453812Salfred		_thread_kern_sig_undefer();
12553812Salfred
12653812Salfred		ret = 0;
12753812Salfred	}
12853812Salfred	return (ret);
12953812Salfred}
13053812Salfred
13153812Salfredint
13271581Sdeischen_pthread_setcancelstate(int state, int *oldstate)
13353812Salfred{
13471581Sdeischen	struct pthread	*curthread = _get_curthread();
13553812Salfred	int ostate;
13653812Salfred	int ret;
13753812Salfred
13871581Sdeischen	ostate = curthread->cancelflags & PTHREAD_CANCEL_DISABLE;
13953812Salfred
14053812Salfred	switch (state) {
14153812Salfred	case PTHREAD_CANCEL_ENABLE:
14253812Salfred		if (oldstate != NULL)
14353812Salfred			*oldstate = ostate;
14471581Sdeischen		curthread->cancelflags &= ~PTHREAD_CANCEL_DISABLE;
14571581Sdeischen		if ((curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0)
14653812Salfred			pthread_testcancel();
14753812Salfred		ret = 0;
14853812Salfred		break;
14953812Salfred	case PTHREAD_CANCEL_DISABLE:
15053812Salfred		if (oldstate != NULL)
15153812Salfred			*oldstate = ostate;
15271581Sdeischen		curthread->cancelflags |= PTHREAD_CANCEL_DISABLE;
15353812Salfred		ret = 0;
15453812Salfred		break;
15553812Salfred	default:
15653812Salfred		ret = EINVAL;
15753812Salfred	}
15853812Salfred
15953812Salfred	return (ret);
16053812Salfred}
16153812Salfred
16253812Salfredint
16371581Sdeischen_pthread_setcanceltype(int type, int *oldtype)
16453812Salfred{
16571581Sdeischen	struct pthread	*curthread = _get_curthread();
16653812Salfred	int otype;
16753812Salfred	int ret;
16853812Salfred
16971581Sdeischen	otype = curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS;
17053812Salfred	switch (type) {
17153812Salfred	case PTHREAD_CANCEL_ASYNCHRONOUS:
17253812Salfred		if (oldtype != NULL)
17353812Salfred			*oldtype = otype;
17471581Sdeischen		curthread->cancelflags |= PTHREAD_CANCEL_ASYNCHRONOUS;
17553812Salfred		pthread_testcancel();
17653812Salfred		ret = 0;
17753812Salfred		break;
17853812Salfred	case PTHREAD_CANCEL_DEFERRED:
17953812Salfred		if (oldtype != NULL)
18053812Salfred			*oldtype = otype;
18171581Sdeischen		curthread->cancelflags &= ~PTHREAD_CANCEL_ASYNCHRONOUS;
18253812Salfred		ret = 0;
18353812Salfred		break;
18453812Salfred	default:
18553812Salfred		ret = EINVAL;
18653812Salfred	}
18753812Salfred
18853812Salfred	return (ret);
18953812Salfred}
19053812Salfred
19153812Salfredvoid
19271581Sdeischen_pthread_testcancel(void)
19353812Salfred{
19471581Sdeischen	struct pthread	*curthread = _get_curthread();
19571581Sdeischen
19671581Sdeischen	if (((curthread->cancelflags & PTHREAD_CANCEL_DISABLE) == 0) &&
19771581Sdeischen	    ((curthread->cancelflags & PTHREAD_CANCELLING) != 0)) {
19853812Salfred		/*
19953812Salfred		 * It is possible for this thread to be swapped out
20053812Salfred		 * while performing cancellation; do not allow it
20153812Salfred		 * to be cancelled again.
20253812Salfred		 */
20371581Sdeischen		curthread->cancelflags &= ~PTHREAD_CANCELLING;
20453812Salfred		_thread_exit_cleanup();
20553812Salfred		pthread_exit(PTHREAD_CANCELED);
20653812Salfred		PANIC("cancel");
20753812Salfred	}
20853812Salfred}
20953812Salfred
21053812Salfredvoid
21153812Salfred_thread_enter_cancellation_point(void)
21253812Salfred{
21371581Sdeischen	struct pthread	*curthread = _get_curthread();
21471581Sdeischen
21553812Salfred	/* Look for a cancellation before we block: */
21653812Salfred	pthread_testcancel();
21771581Sdeischen	curthread->cancelflags |= PTHREAD_AT_CANCEL_POINT;
21853812Salfred}
21953812Salfred
22053812Salfredvoid
22153812Salfred_thread_leave_cancellation_point(void)
22253812Salfred{
22371581Sdeischen	struct pthread	*curthread = _get_curthread();
22471581Sdeischen
22571581Sdeischen	curthread->cancelflags &= ~PTHREAD_AT_CANCEL_POINT;
22653812Salfred	/* Look for a cancellation after we unblock: */
22753812Salfred	pthread_testcancel();
22853812Salfred}
22956277Sjasone
23056277Sjasonestatic void
23156277Sjasonefinish_cancellation(void *arg)
23256277Sjasone{
23371581Sdeischen	struct pthread	*curthread = _get_curthread();
23456277Sjasone
23571581Sdeischen	curthread->continuation = NULL;
23671581Sdeischen	curthread->interrupted = 0;
23771581Sdeischen
23871581Sdeischen	if ((curthread->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) {
23971581Sdeischen		curthread->cancelflags &= ~PTHREAD_CANCEL_NEEDED;
24056277Sjasone		_thread_exit_cleanup();
24156277Sjasone		pthread_exit(PTHREAD_CANCELED);
24256277Sjasone	}
24356277Sjasone}
244