thr_cancel.c revision 81750
153812Salfred/*
253812Salfred * David Leonard <d@openbsd.org>, 1999. Public domain.
353812Salfred * $FreeBSD: head/lib/libkse/thread/thr_cancel.c 81750 2001-08-16 06:31:32Z jasone $
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				/*
6881750Sjasone				 * Disconnect the thread from the joinee and
6981750Sjasone				 * detach:
7081750Sjasone				 */
7181750Sjasone				if (pthread->data.thread != NULL) {
7281750Sjasone					pthread->data.thread->joiner = NULL;
7381750Sjasone					pthread_detach((pthread_t)
7481750Sjasone					    pthread->data.thread);
7581750Sjasone				}
7681750Sjasone				pthread->cancelflags |= PTHREAD_CANCELLING;
7781750Sjasone				PTHREAD_NEW_STATE(pthread, PS_RUNNING);
7881750Sjasone				break;
7981750Sjasone
8061681Sjasone			case PS_SUSPENDED:
8161681Sjasone				if (pthread->suspended == SUSP_NO ||
8261681Sjasone				    pthread->suspended == SUSP_YES ||
8381750Sjasone				    pthread->suspended == SUSP_JOIN ||
8461681Sjasone				    pthread->suspended == SUSP_NOWAIT) {
8561681Sjasone					/*
8661681Sjasone					 * This thread isn't in any scheduling
8761681Sjasone					 * queues; just change it's state:
8861681Sjasone					 */
8961681Sjasone					pthread->cancelflags |=
9061681Sjasone					    PTHREAD_CANCELLING;
9161681Sjasone					PTHREAD_SET_STATE(pthread, PS_RUNNING);
9261681Sjasone					break;
9361681Sjasone				}
9461681Sjasone				/* FALLTHROUGH */
9553812Salfred			case PS_MUTEX_WAIT:
9653812Salfred			case PS_COND_WAIT:
9753812Salfred			case PS_FDLR_WAIT:
9853812Salfred			case PS_FDLW_WAIT:
9953812Salfred			case PS_FILE_WAIT:
10053812Salfred				/*
10153812Salfred				 * Threads in these states may be in queues.
10253812Salfred				 * In order to preserve queue integrity, the
10353812Salfred				 * cancelled thread must remove itself from the
10453812Salfred				 * queue.  Mark the thread as interrupted and
10553812Salfred				 * needing cancellation, and set the state to
10653812Salfred				 * running.  When the thread resumes, it will
10756277Sjasone				 * remove itself from the queue and call the
10856277Sjasone				 * cancellation completion routine.
10953812Salfred				 */
11053812Salfred				pthread->interrupted = 1;
11153812Salfred				pthread->cancelflags |= PTHREAD_CANCEL_NEEDED;
11253812Salfred				PTHREAD_NEW_STATE(pthread,PS_RUNNING);
11356277Sjasone				pthread->continuation = finish_cancellation;
11453812Salfred				break;
11553812Salfred
11653812Salfred			case PS_DEAD:
11753812Salfred			case PS_DEADLOCK:
11853812Salfred			case PS_STATE_MAX:
11953812Salfred				/* Ignore - only here to silence -Wall: */
12053812Salfred				break;
12154708Sdeischen			}
12253812Salfred		}
12354708Sdeischen
12453812Salfred		/* Unprotect the scheduling queues: */
12553812Salfred		_thread_kern_sig_undefer();
12653812Salfred
12753812Salfred		ret = 0;
12853812Salfred	}
12953812Salfred	return (ret);
13053812Salfred}
13153812Salfred
13253812Salfredint
13371581Sdeischen_pthread_setcancelstate(int state, int *oldstate)
13453812Salfred{
13571581Sdeischen	struct pthread	*curthread = _get_curthread();
13653812Salfred	int ostate;
13753812Salfred	int ret;
13853812Salfred
13971581Sdeischen	ostate = curthread->cancelflags & PTHREAD_CANCEL_DISABLE;
14053812Salfred
14153812Salfred	switch (state) {
14253812Salfred	case PTHREAD_CANCEL_ENABLE:
14353812Salfred		if (oldstate != NULL)
14453812Salfred			*oldstate = ostate;
14571581Sdeischen		curthread->cancelflags &= ~PTHREAD_CANCEL_DISABLE;
14671581Sdeischen		if ((curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0)
14753812Salfred			pthread_testcancel();
14853812Salfred		ret = 0;
14953812Salfred		break;
15053812Salfred	case PTHREAD_CANCEL_DISABLE:
15153812Salfred		if (oldstate != NULL)
15253812Salfred			*oldstate = ostate;
15371581Sdeischen		curthread->cancelflags |= PTHREAD_CANCEL_DISABLE;
15453812Salfred		ret = 0;
15553812Salfred		break;
15653812Salfred	default:
15753812Salfred		ret = EINVAL;
15853812Salfred	}
15953812Salfred
16053812Salfred	return (ret);
16153812Salfred}
16253812Salfred
16353812Salfredint
16471581Sdeischen_pthread_setcanceltype(int type, int *oldtype)
16553812Salfred{
16671581Sdeischen	struct pthread	*curthread = _get_curthread();
16753812Salfred	int otype;
16853812Salfred	int ret;
16953812Salfred
17071581Sdeischen	otype = curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS;
17153812Salfred	switch (type) {
17253812Salfred	case PTHREAD_CANCEL_ASYNCHRONOUS:
17353812Salfred		if (oldtype != NULL)
17453812Salfred			*oldtype = otype;
17571581Sdeischen		curthread->cancelflags |= PTHREAD_CANCEL_ASYNCHRONOUS;
17653812Salfred		pthread_testcancel();
17753812Salfred		ret = 0;
17853812Salfred		break;
17953812Salfred	case PTHREAD_CANCEL_DEFERRED:
18053812Salfred		if (oldtype != NULL)
18153812Salfred			*oldtype = otype;
18271581Sdeischen		curthread->cancelflags &= ~PTHREAD_CANCEL_ASYNCHRONOUS;
18353812Salfred		ret = 0;
18453812Salfred		break;
18553812Salfred	default:
18653812Salfred		ret = EINVAL;
18753812Salfred	}
18853812Salfred
18953812Salfred	return (ret);
19053812Salfred}
19153812Salfred
19253812Salfredvoid
19371581Sdeischen_pthread_testcancel(void)
19453812Salfred{
19571581Sdeischen	struct pthread	*curthread = _get_curthread();
19671581Sdeischen
19771581Sdeischen	if (((curthread->cancelflags & PTHREAD_CANCEL_DISABLE) == 0) &&
19871581Sdeischen	    ((curthread->cancelflags & PTHREAD_CANCELLING) != 0)) {
19953812Salfred		/*
20053812Salfred		 * It is possible for this thread to be swapped out
20153812Salfred		 * while performing cancellation; do not allow it
20253812Salfred		 * to be cancelled again.
20353812Salfred		 */
20471581Sdeischen		curthread->cancelflags &= ~PTHREAD_CANCELLING;
20553812Salfred		_thread_exit_cleanup();
20653812Salfred		pthread_exit(PTHREAD_CANCELED);
20753812Salfred		PANIC("cancel");
20853812Salfred	}
20953812Salfred}
21053812Salfred
21153812Salfredvoid
21253812Salfred_thread_enter_cancellation_point(void)
21353812Salfred{
21471581Sdeischen	struct pthread	*curthread = _get_curthread();
21571581Sdeischen
21653812Salfred	/* Look for a cancellation before we block: */
21753812Salfred	pthread_testcancel();
21871581Sdeischen	curthread->cancelflags |= PTHREAD_AT_CANCEL_POINT;
21953812Salfred}
22053812Salfred
22153812Salfredvoid
22253812Salfred_thread_leave_cancellation_point(void)
22353812Salfred{
22471581Sdeischen	struct pthread	*curthread = _get_curthread();
22571581Sdeischen
22671581Sdeischen	curthread->cancelflags &= ~PTHREAD_AT_CANCEL_POINT;
22753812Salfred	/* Look for a cancellation after we unblock: */
22853812Salfred	pthread_testcancel();
22953812Salfred}
23056277Sjasone
23156277Sjasonestatic void
23256277Sjasonefinish_cancellation(void *arg)
23356277Sjasone{
23471581Sdeischen	struct pthread	*curthread = _get_curthread();
23556277Sjasone
23671581Sdeischen	curthread->continuation = NULL;
23771581Sdeischen	curthread->interrupted = 0;
23871581Sdeischen
23971581Sdeischen	if ((curthread->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) {
24071581Sdeischen		curthread->cancelflags &= ~PTHREAD_CANCEL_NEEDED;
24156277Sjasone		_thread_exit_cleanup();
24256277Sjasone		pthread_exit(PTHREAD_CANCELED);
24356277Sjasone	}
24456277Sjasone}
245