thr_cancel.c revision 123312
138032Speter/*
264562Sgshapiro * David Leonard <d@openbsd.org>, 1999. Public domain.
364562Sgshapiro * $FreeBSD: head/lib/libkse/thread/thr_cancel.c 123312 2003-12-09 02:20:56Z davidxu $
438032Speter */
538032Speter#include <sys/errno.h>
638032Speter#include <pthread.h>
738032Speter#include "thr_private.h"
838032Speter
938032Speter__weak_reference(_pthread_cancel, pthread_cancel);
1038032Speter__weak_reference(_pthread_setcancelstate, pthread_setcancelstate);
1138032Speter__weak_reference(_pthread_setcanceltype, pthread_setcanceltype);
1238032Speter__weak_reference(_pthread_testcancel, pthread_testcancel);
1338032Speter
1438032Speterstatic inline int
1538032Spetercheckcancel(struct pthread *curthread)
1664562Sgshapiro{
1764562Sgshapiro	if (((curthread->cancelflags & PTHREAD_CANCEL_DISABLE) == 0) &&
1838032Speter	    ((curthread->cancelflags & THR_CANCELLING) != 0)) {
1938032Speter		/*
2038032Speter		 * It is possible for this thread to be swapped out
2164562Sgshapiro		 * while performing cancellation; do not allow it
2238032Speter		 * to be cancelled again.
2338032Speter		 */
2466494Sgshapiro		curthread->cancelflags &= ~THR_CANCELLING;
2564562Sgshapiro		return (1);
2638032Speter	}
2738032Speter	else
2838032Speter		return (0);
2964562Sgshapiro}
3038032Speter
3164562Sgshapirostatic inline void
3264562Sgshapirotestcancel(struct pthread *curthread)
3364562Sgshapiro{
3464562Sgshapiro	if (checkcancel(curthread) != 0) {
3564562Sgshapiro		/* Unlock before exiting: */
3664562Sgshapiro		THR_THREAD_UNLOCK(curthread, curthread);
3764562Sgshapiro
3864562Sgshapiro		_thr_exit_cleanup();
3964562Sgshapiro		pthread_exit(PTHREAD_CANCELED);
4038032Speter		PANIC("cancel");
4138032Speter	}
4238032Speter}
4338032Speter
4438032Speterint
4538032Speter_pthread_cancel(pthread_t pthread)
4638032Speter{
4738032Speter	struct pthread *curthread = _get_curthread();
4864562Sgshapiro	struct pthread *joinee = NULL;
4938032Speter	struct kse_mailbox *kmbx = NULL;
5038032Speter	int ret;
5138032Speter
5238032Speter	if ((ret = _thr_ref_add(curthread, pthread, /*include dead*/0)) == 0) {
5338032Speter		/*
5438032Speter		 * Take the thread's lock while we change the cancel flags.
5538032Speter		 */
5638032Speter		THR_THREAD_LOCK(curthread, pthread);
5738032Speter		THR_SCHED_LOCK(curthread, pthread);
5838032Speter		if (pthread->flags & THR_FLAGS_EXITING) {
5938032Speter			THR_SCHED_UNLOCK(curthread, pthread);
6038032Speter			THR_THREAD_UNLOCK(curthread, pthread);
6138032Speter			_thr_ref_delete(curthread, pthread);
6238032Speter			return (ESRCH);
6338032Speter		}
6438032Speter		if (((pthread->cancelflags & PTHREAD_CANCEL_DISABLE) != 0) ||
6538032Speter		    (((pthread->cancelflags & THR_AT_CANCEL_POINT) == 0) &&
6638032Speter		    ((pthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) == 0)))
6738032Speter			/* Just mark it for cancellation: */
6838032Speter			pthread->cancelflags |= THR_CANCELLING;
6938032Speter		else {
7038032Speter			/*
7138032Speter			 * Check if we need to kick it back into the
7238032Speter			 * run queue:
7338032Speter			 */
7464562Sgshapiro			switch (pthread->state) {
7538032Speter			case PS_RUNNING:
7638032Speter				/* No need to resume: */
7738032Speter				pthread->cancelflags |= THR_CANCELLING;
7838032Speter				break;
7938032Speter
8064562Sgshapiro			case PS_LOCKWAIT:
8138032Speter				/*
8238032Speter				 * These can't be removed from the queue.
8338032Speter				 * Just mark it as cancelling and tell it
8464562Sgshapiro				 * to yield once it leaves the critical
8538032Speter				 * region.
8638032Speter				 */
8738032Speter				pthread->cancelflags |= THR_CANCELLING;
8838032Speter				pthread->critical_yield = 1;
8938032Speter				break;
9038032Speter
9164562Sgshapiro			case PS_SLEEP_WAIT:
9238032Speter			case PS_SIGSUSPEND:
9364562Sgshapiro			case PS_SIGWAIT:
9438032Speter				/* Interrupt and resume: */
9564562Sgshapiro				pthread->interrupted = 1;
9664562Sgshapiro				pthread->cancelflags |= THR_CANCELLING;
9764562Sgshapiro				kmbx = _thr_setrunnable_unlocked(pthread);
9864562Sgshapiro				break;
9964562Sgshapiro
10064562Sgshapiro			case PS_JOIN:
10164562Sgshapiro				/* Disconnect the thread from the joinee: */
10266494Sgshapiro				joinee = pthread->join_status.thread;
10364562Sgshapiro				pthread->join_status.thread = NULL;
10464562Sgshapiro				pthread->cancelflags |= THR_CANCELLING;
10564562Sgshapiro				kmbx = _thr_setrunnable_unlocked(pthread);
10664562Sgshapiro				if ((joinee != NULL) &&
10738032Speter				    (pthread->kseg == joinee->kseg)) {
10838032Speter					/* Remove the joiner from the joinee. */
10938032Speter					joinee->joiner = NULL;
11038032Speter					joinee = NULL;
11138032Speter				}
11238032Speter				break;
11338032Speter
11438032Speter			case PS_SUSPENDED:
11538032Speter			case PS_MUTEX_WAIT:
11638032Speter			case PS_COND_WAIT:
11738032Speter				/*
11838032Speter				 * Threads in these states may be in queues.
11938032Speter				 * In order to preserve queue integrity, the
12064562Sgshapiro				 * cancelled thread must remove itself from the
12138032Speter				 * queue.  Mark the thread as interrupted and
12264562Sgshapiro				 * needing cancellation, and set the state to
12338032Speter				 * running.  When the thread resumes, it will
12464562Sgshapiro				 * remove itself from the queue and call the
12538032Speter				 * cancellation completion routine.
12638032Speter				 */
12738032Speter				pthread->interrupted = 1;
12838032Speter				pthread->cancelflags |= THR_CANCEL_NEEDED;
12938032Speter				kmbx = _thr_setrunnable_unlocked(pthread);
13038032Speter				pthread->continuation =
13164562Sgshapiro					_thr_finish_cancellation;
13264562Sgshapiro				break;
13338032Speter
13464562Sgshapiro			case PS_DEAD:
13538032Speter			case PS_DEADLOCK:
13638032Speter			case PS_STATE_MAX:
13738032Speter				/* Ignore - only here to silence -Wall: */
13866494Sgshapiro				break;
13966494Sgshapiro			}
14066494Sgshapiro			if ((pthread->cancelflags & THR_AT_CANCEL_POINT) &&
14138032Speter			    (pthread->blocked != 0 ||
14238032Speter			     pthread->attr.flags & PTHREAD_SCOPE_SYSTEM))
14338032Speter				kse_thr_interrupt(&pthread->tcb->tcb_tmbx,
14438032Speter					KSE_INTR_INTERRUPT, 0);
14538032Speter		}
14638032Speter
14738032Speter		/*
14838032Speter		 * Release the thread's lock and remove the
14938032Speter		 * reference:
15038032Speter		 */
15138032Speter		THR_SCHED_UNLOCK(curthread, pthread);
15238032Speter		THR_THREAD_UNLOCK(curthread, pthread);
15338032Speter		_thr_ref_delete(curthread, pthread);
15438032Speter		if (kmbx != NULL)
15538032Speter			kse_wakeup(kmbx);
15638032Speter
15738032Speter		if ((joinee != NULL) &&
15838032Speter		    (_thr_ref_add(curthread, joinee, /* include dead */1) == 0)) {
15938032Speter			/* Remove the joiner from the joinee. */
16038032Speter			THR_SCHED_LOCK(curthread, joinee);
16138032Speter			joinee->joiner = NULL;
16238032Speter			THR_SCHED_UNLOCK(curthread, joinee);
16338032Speter			_thr_ref_delete(curthread, joinee);
16438032Speter		}
16538032Speter	}
16638032Speter	return (ret);
16764562Sgshapiro}
16838032Speter
16938032Speterint
17038032Speter_pthread_setcancelstate(int state, int *oldstate)
17138032Speter{
17238032Speter	struct pthread	*curthread = _get_curthread();
17338032Speter	int ostate;
17438032Speter	int ret;
17538032Speter	int need_exit = 0;
17638032Speter
17738032Speter	/* Take the thread's lock while fiddling with the state: */
17838032Speter	THR_THREAD_LOCK(curthread, curthread);
17938032Speter
18038032Speter	ostate = curthread->cancelflags & PTHREAD_CANCEL_DISABLE;
18138032Speter
18238032Speter	switch (state) {
18338032Speter	case PTHREAD_CANCEL_ENABLE:
18438032Speter		curthread->cancelflags &= ~PTHREAD_CANCEL_DISABLE;
18538032Speter		if ((curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0)
18638032Speter			need_exit = checkcancel(curthread);
18738032Speter		ret = 0;
18838032Speter		break;
18938032Speter	case PTHREAD_CANCEL_DISABLE:
19038032Speter		curthread->cancelflags |= PTHREAD_CANCEL_DISABLE;
19164562Sgshapiro		ret = 0;
19238032Speter		break;
19364562Sgshapiro	default:
19438032Speter		ret = EINVAL;
19564562Sgshapiro	}
19664562Sgshapiro
19738032Speter	THR_THREAD_UNLOCK(curthread, curthread);
19838032Speter	if (need_exit != 0) {
19938032Speter		_thr_exit_cleanup();
20038032Speter		pthread_exit(PTHREAD_CANCELED);
20138032Speter		PANIC("cancel");
20238032Speter	}
20338032Speter	if (ret == 0 && oldstate != NULL)
20464562Sgshapiro		*oldstate = ostate;
20538032Speter
20664562Sgshapiro	return (ret);
20738032Speter}
20864562Sgshapiro
20938032Speterint
21038032Speter_pthread_setcanceltype(int type, int *oldtype)
21138032Speter{
21238032Speter	struct pthread	*curthread = _get_curthread();
21338032Speter	int otype;
21438032Speter	int ret;
21538032Speter	int need_exit = 0;
21664562Sgshapiro
21738032Speter	/* Take the thread's lock while fiddling with the state: */
21838032Speter	THR_THREAD_LOCK(curthread, curthread);
21964562Sgshapiro
22038032Speter	otype = curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS;
22164562Sgshapiro	switch (type) {
22264562Sgshapiro	case PTHREAD_CANCEL_ASYNCHRONOUS:
22364562Sgshapiro		curthread->cancelflags |= PTHREAD_CANCEL_ASYNCHRONOUS;
22464562Sgshapiro		need_exit = checkcancel(curthread);
22564562Sgshapiro		ret = 0;
22664562Sgshapiro		break;
22764562Sgshapiro	case PTHREAD_CANCEL_DEFERRED:
22864562Sgshapiro		curthread->cancelflags &= ~PTHREAD_CANCEL_ASYNCHRONOUS;
22938032Speter		ret = 0;
23038032Speter		break;
23138032Speter	default:
23238032Speter		ret = EINVAL;
23338032Speter	}
23438032Speter
23538032Speter	THR_THREAD_UNLOCK(curthread, curthread);
23638032Speter	if (need_exit != 0) {
23738032Speter		_thr_exit_cleanup();
23864562Sgshapiro		pthread_exit(PTHREAD_CANCELED);
23938032Speter		PANIC("cancel");
24038032Speter	}
24164562Sgshapiro	if (ret == 0 && oldtype != NULL)
24264562Sgshapiro		*oldtype = otype;
24338032Speter
24464562Sgshapiro	return (ret);
24538032Speter}
24664562Sgshapiro
24764562Sgshapirovoid
24838032Speter_pthread_testcancel(void)
24938032Speter{
25038032Speter	struct pthread	*curthread = _get_curthread();
25138032Speter
25238032Speter	THR_THREAD_LOCK(curthread, curthread);
25338032Speter	testcancel(curthread);
25438032Speter	THR_THREAD_UNLOCK(curthread, curthread);
25538032Speter}
25638032Speter
25738032Spetervoid
25838032Speter_thr_cancel_enter(struct pthread *thread)
25964562Sgshapiro{
26038032Speter	/* Look for a cancellation before we block: */
26164562Sgshapiro	THR_THREAD_LOCK(thread, thread);
26264562Sgshapiro	testcancel(thread);
26338032Speter	thread->cancelflags |= THR_AT_CANCEL_POINT;
26464562Sgshapiro	THR_THREAD_UNLOCK(thread, thread);
26564562Sgshapiro}
26638032Speter
26764562Sgshapirovoid
26864562Sgshapiro_thr_cancel_leave(struct pthread *thread, int check)
26938032Speter{
27038032Speter	THR_THREAD_LOCK(thread, thread);
27138032Speter	thread->cancelflags &= ~THR_AT_CANCEL_POINT;
27238032Speter	/* Look for a cancellation after we unblock: */
27338032Speter	if (check)
27438032Speter		testcancel(thread);
27538032Speter	THR_THREAD_UNLOCK(thread, thread);
27638032Speter}
27738032Speter
27838032Spetervoid
27938032Speter_thr_finish_cancellation(void *arg)
28038032Speter{
28138032Speter	struct pthread	*curthread = _get_curthread();
28238032Speter
28338032Speter	curthread->continuation = NULL;
28438032Speter	curthread->interrupted = 0;
28564562Sgshapiro
28664562Sgshapiro	THR_THREAD_LOCK(curthread, curthread);
28764562Sgshapiro	if ((curthread->cancelflags & THR_CANCEL_NEEDED) != 0) {
28864562Sgshapiro		curthread->cancelflags &= ~THR_CANCEL_NEEDED;
28964562Sgshapiro		THR_THREAD_UNLOCK(curthread, curthread);
29064562Sgshapiro		_thr_exit_cleanup();
29164562Sgshapiro		pthread_exit(PTHREAD_CANCELED);
29264562Sgshapiro	}
29364562Sgshapiro	THR_THREAD_UNLOCK(curthread, curthread);
29464562Sgshapiro}
29564562Sgshapiro