thr_cancel.c revision 111035
153812Salfred/*
253812Salfred * David Leonard <d@openbsd.org>, 1999. Public domain.
353812Salfred * $FreeBSD: head/lib/libkse/thread/thr_cancel.c 111035 2003-02-17 10:05:18Z mini $
453812Salfred */
553812Salfred#include <sys/errno.h>
653812Salfred#include <pthread.h>
7103388Smini#include "thr_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 */
2395947Sarchie	} else if (pthread->state == PS_DEAD || pthread->state == PS_DEADLOCK
2495947Sarchie	    || (pthread->flags & PTHREAD_EXITING) != 0) {
2553812Salfred		ret = 0;
2653812Salfred	} else {
2753812Salfred		/* Protect the scheduling queues: */
2853812Salfred		_thread_kern_sig_defer();
2953812Salfred
3054708Sdeischen		if (((pthread->cancelflags & PTHREAD_CANCEL_DISABLE) != 0) ||
3154708Sdeischen		    (((pthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) == 0) &&
3254708Sdeischen		    ((pthread->cancelflags & PTHREAD_AT_CANCEL_POINT) == 0)))
3354708Sdeischen			/* Just mark it for cancellation: */
3454708Sdeischen			pthread->cancelflags |= PTHREAD_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: */
4353812Salfred				pthread->cancelflags |= PTHREAD_CANCELLING;
4453812Salfred				break;
4553812Salfred
4653812Salfred			case PS_SPINBLOCK:
4754708Sdeischen				/* Remove these threads from the work queue: */
4853812Salfred				if ((pthread->flags & PTHREAD_FLAGS_IN_WORKQ)
4953812Salfred				    != 0)
5053812Salfred					PTHREAD_WORKQ_REMOVE(pthread);
5153812Salfred				/* Fall through: */
5253812Salfred			case PS_SLEEP_WAIT:
5353812Salfred			case PS_WAIT_WAIT:
54111035Smini			case PS_SIGSUSPEND:
55111035Smini			case PS_SIGWAIT:
5653812Salfred				/* Interrupt and resume: */
5753812Salfred				pthread->interrupted = 1;
5853812Salfred				pthread->cancelflags |= PTHREAD_CANCELLING;
5953812Salfred				PTHREAD_NEW_STATE(pthread,PS_RUNNING);
6053812Salfred				break;
6153812Salfred
6276909Sjasone			case PS_JOIN:
6381750Sjasone				/*
6488015Sdeischen				 * Disconnect the thread from the joinee:
6581750Sjasone				 */
6687988Sdeischen				if (pthread->join_status.thread != NULL) {
6787988Sdeischen					pthread->join_status.thread->joiner
6887988Sdeischen					    = NULL;
6991762Sdeischen					pthread->join_status.thread = NULL;
7081750Sjasone				}
7181750Sjasone				pthread->cancelflags |= PTHREAD_CANCELLING;
7281750Sjasone				PTHREAD_NEW_STATE(pthread, PS_RUNNING);
7381750Sjasone				break;
7481750Sjasone
7561681Sjasone			case PS_SUSPENDED:
7653812Salfred			case PS_MUTEX_WAIT:
7753812Salfred			case PS_COND_WAIT:
7853812Salfred				/*
7953812Salfred				 * Threads in these states may be in queues.
8053812Salfred				 * In order to preserve queue integrity, the
8153812Salfred				 * cancelled thread must remove itself from the
8253812Salfred				 * queue.  Mark the thread as interrupted and
8353812Salfred				 * needing cancellation, and set the state to
8453812Salfred				 * running.  When the thread resumes, it will
8556277Sjasone				 * remove itself from the queue and call the
8656277Sjasone				 * cancellation completion routine.
8753812Salfred				 */
8853812Salfred				pthread->interrupted = 1;
8953812Salfred				pthread->cancelflags |= PTHREAD_CANCEL_NEEDED;
9097204Sdeischen				PTHREAD_NEW_STATE(pthread, PS_RUNNING);
9156277Sjasone				pthread->continuation = finish_cancellation;
9253812Salfred				break;
9353812Salfred
9453812Salfred			case PS_DEAD:
9553812Salfred			case PS_DEADLOCK:
9653812Salfred			case PS_STATE_MAX:
9753812Salfred				/* Ignore - only here to silence -Wall: */
9853812Salfred				break;
9954708Sdeischen			}
10053812Salfred		}
10154708Sdeischen
10253812Salfred		/* Unprotect the scheduling queues: */
10353812Salfred		_thread_kern_sig_undefer();
10453812Salfred
10553812Salfred		ret = 0;
10653812Salfred	}
10753812Salfred	return (ret);
10853812Salfred}
10953812Salfred
11053812Salfredint
11171581Sdeischen_pthread_setcancelstate(int state, int *oldstate)
11253812Salfred{
11371581Sdeischen	struct pthread	*curthread = _get_curthread();
11453812Salfred	int ostate;
11553812Salfred	int ret;
11653812Salfred
11771581Sdeischen	ostate = curthread->cancelflags & PTHREAD_CANCEL_DISABLE;
11853812Salfred
11953812Salfred	switch (state) {
12053812Salfred	case PTHREAD_CANCEL_ENABLE:
12153812Salfred		if (oldstate != NULL)
12253812Salfred			*oldstate = ostate;
12371581Sdeischen		curthread->cancelflags &= ~PTHREAD_CANCEL_DISABLE;
12471581Sdeischen		if ((curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0)
12553812Salfred			pthread_testcancel();
12653812Salfred		ret = 0;
12753812Salfred		break;
12853812Salfred	case PTHREAD_CANCEL_DISABLE:
12953812Salfred		if (oldstate != NULL)
13053812Salfred			*oldstate = ostate;
13171581Sdeischen		curthread->cancelflags |= PTHREAD_CANCEL_DISABLE;
13253812Salfred		ret = 0;
13353812Salfred		break;
13453812Salfred	default:
13553812Salfred		ret = EINVAL;
13653812Salfred	}
13753812Salfred
13853812Salfred	return (ret);
13953812Salfred}
14053812Salfred
14153812Salfredint
14271581Sdeischen_pthread_setcanceltype(int type, int *oldtype)
14353812Salfred{
14471581Sdeischen	struct pthread	*curthread = _get_curthread();
14553812Salfred	int otype;
14653812Salfred	int ret;
14753812Salfred
14871581Sdeischen	otype = curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS;
14953812Salfred	switch (type) {
15053812Salfred	case PTHREAD_CANCEL_ASYNCHRONOUS:
15153812Salfred		if (oldtype != NULL)
15253812Salfred			*oldtype = otype;
15371581Sdeischen		curthread->cancelflags |= PTHREAD_CANCEL_ASYNCHRONOUS;
15453812Salfred		pthread_testcancel();
15553812Salfred		ret = 0;
15653812Salfred		break;
15753812Salfred	case PTHREAD_CANCEL_DEFERRED:
15853812Salfred		if (oldtype != NULL)
15953812Salfred			*oldtype = otype;
16071581Sdeischen		curthread->cancelflags &= ~PTHREAD_CANCEL_ASYNCHRONOUS;
16153812Salfred		ret = 0;
16253812Salfred		break;
16353812Salfred	default:
16453812Salfred		ret = EINVAL;
16553812Salfred	}
16653812Salfred
16753812Salfred	return (ret);
16853812Salfred}
16953812Salfred
17053812Salfredvoid
17171581Sdeischen_pthread_testcancel(void)
17253812Salfred{
17371581Sdeischen	struct pthread	*curthread = _get_curthread();
17471581Sdeischen
17571581Sdeischen	if (((curthread->cancelflags & PTHREAD_CANCEL_DISABLE) == 0) &&
17695947Sarchie	    ((curthread->cancelflags & PTHREAD_CANCELLING) != 0) &&
17795947Sarchie	    ((curthread->flags & PTHREAD_EXITING) == 0)) {
17853812Salfred		/*
17953812Salfred		 * It is possible for this thread to be swapped out
18053812Salfred		 * while performing cancellation; do not allow it
18153812Salfred		 * to be cancelled again.
18253812Salfred		 */
18371581Sdeischen		curthread->cancelflags &= ~PTHREAD_CANCELLING;
18453812Salfred		_thread_exit_cleanup();
18553812Salfred		pthread_exit(PTHREAD_CANCELED);
18653812Salfred		PANIC("cancel");
18753812Salfred	}
18853812Salfred}
18953812Salfred
19053812Salfredvoid
19153812Salfred_thread_enter_cancellation_point(void)
19253812Salfred{
19371581Sdeischen	struct pthread	*curthread = _get_curthread();
19471581Sdeischen
19553812Salfred	/* Look for a cancellation before we block: */
19653812Salfred	pthread_testcancel();
19771581Sdeischen	curthread->cancelflags |= PTHREAD_AT_CANCEL_POINT;
19853812Salfred}
19953812Salfred
20053812Salfredvoid
20153812Salfred_thread_leave_cancellation_point(void)
20253812Salfred{
20371581Sdeischen	struct pthread	*curthread = _get_curthread();
20471581Sdeischen
20571581Sdeischen	curthread->cancelflags &= ~PTHREAD_AT_CANCEL_POINT;
20653812Salfred	/* Look for a cancellation after we unblock: */
20753812Salfred	pthread_testcancel();
20853812Salfred}
20956277Sjasone
21056277Sjasonestatic void
21156277Sjasonefinish_cancellation(void *arg)
21256277Sjasone{
21371581Sdeischen	struct pthread	*curthread = _get_curthread();
21456277Sjasone
21571581Sdeischen	curthread->continuation = NULL;
21671581Sdeischen	curthread->interrupted = 0;
21771581Sdeischen
21871581Sdeischen	if ((curthread->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) {
21971581Sdeischen		curthread->cancelflags &= ~PTHREAD_CANCEL_NEEDED;
22056277Sjasone		_thread_exit_cleanup();
22156277Sjasone		pthread_exit(PTHREAD_CANCELED);
22256277Sjasone	}
22356277Sjasone}
224