153812Salfred/*
253812Salfred * David Leonard <d@openbsd.org>, 1999. Public domain.
353812Salfred * $FreeBSD$
453812Salfred */
5174112Sdeischen#include "namespace.h"
653812Salfred#include <sys/errno.h>
753812Salfred#include <pthread.h>
8174112Sdeischen#include "un-namespace.h"
9103388Smini#include "thr_private.h"
1053812Salfred
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
16123312Sdavidxustatic inline int
17123312Sdavidxucheckcancel(struct pthread *curthread)
18123312Sdavidxu{
19139023Sdeischen	if ((curthread->cancelflags & THR_CANCELLING) != 0) {
20123312Sdavidxu		/*
21123312Sdavidxu		 * It is possible for this thread to be swapped out
22123312Sdavidxu		 * while performing cancellation; do not allow it
23123312Sdavidxu		 * to be cancelled again.
24123312Sdavidxu		 */
25139023Sdeischen		if ((curthread->flags & THR_FLAGS_EXITING) != 0) {
26139023Sdeischen			/*
27139023Sdeischen			 * This may happen once, but after this, it
28139023Sdeischen			 * shouldn't happen again.
29139023Sdeischen			 */
30139023Sdeischen			curthread->cancelflags &= ~THR_CANCELLING;
31139023Sdeischen			return (0);
32139023Sdeischen		}
33139023Sdeischen		if ((curthread->cancelflags & PTHREAD_CANCEL_DISABLE) == 0) {
34139023Sdeischen			curthread->cancelflags &= ~THR_CANCELLING;
35139023Sdeischen			return (1);
36139023Sdeischen		}
37123312Sdavidxu	}
38139023Sdeischen	return (0);
39123312Sdavidxu}
40113658Sdeischen
41123312Sdavidxustatic inline void
42123312Sdavidxutestcancel(struct pthread *curthread)
43123312Sdavidxu{
44123312Sdavidxu	if (checkcancel(curthread) != 0) {
45123312Sdavidxu		/* Unlock before exiting: */
46123312Sdavidxu		THR_THREAD_UNLOCK(curthread, curthread);
47123312Sdavidxu
48123312Sdavidxu		_thr_exit_cleanup();
49174112Sdeischen		_pthread_exit(PTHREAD_CANCELED);
50123312Sdavidxu		PANIC("cancel");
51123312Sdavidxu	}
52123312Sdavidxu}
53123312Sdavidxu
5453812Salfredint
5571581Sdeischen_pthread_cancel(pthread_t pthread)
5653812Salfred{
57113658Sdeischen	struct pthread *curthread = _get_curthread();
58115278Sdeischen	struct pthread *joinee = NULL;
59117907Sdeischen	struct kse_mailbox *kmbx = NULL;
6053812Salfred	int ret;
6153812Salfred
62113658Sdeischen	if ((ret = _thr_ref_add(curthread, pthread, /*include dead*/0)) == 0) {
63113658Sdeischen		/*
64120897Sdavidxu		 * Take the thread's lock while we change the cancel flags.
65113658Sdeischen		 */
66120895Sdavidxu		THR_THREAD_LOCK(curthread, pthread);
67113658Sdeischen		THR_SCHED_LOCK(curthread, pthread);
68116972Sdavidxu		if (pthread->flags & THR_FLAGS_EXITING) {
69116972Sdavidxu			THR_SCHED_UNLOCK(curthread, pthread);
70120895Sdavidxu			THR_THREAD_UNLOCK(curthread, pthread);
71116972Sdavidxu			_thr_ref_delete(curthread, pthread);
72116972Sdavidxu			return (ESRCH);
73116972Sdavidxu		}
7454708Sdeischen		if (((pthread->cancelflags & PTHREAD_CANCEL_DISABLE) != 0) ||
75113658Sdeischen		    (((pthread->cancelflags & THR_AT_CANCEL_POINT) == 0) &&
76113658Sdeischen		    ((pthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) == 0)))
7754708Sdeischen			/* Just mark it for cancellation: */
78113658Sdeischen			pthread->cancelflags |= THR_CANCELLING;
7954708Sdeischen		else {
8054708Sdeischen			/*
8154708Sdeischen			 * Check if we need to kick it back into the
8254708Sdeischen			 * run queue:
8354708Sdeischen			 */
8453812Salfred			switch (pthread->state) {
8553812Salfred			case PS_RUNNING:
8653812Salfred				/* No need to resume: */
87113658Sdeischen				pthread->cancelflags |= THR_CANCELLING;
8853812Salfred				break;
8953812Salfred
90113658Sdeischen			case PS_LOCKWAIT:
91113658Sdeischen				/*
92113658Sdeischen				 * These can't be removed from the queue.
93113658Sdeischen				 * Just mark it as cancelling and tell it
94113658Sdeischen				 * to yield once it leaves the critical
95113658Sdeischen				 * region.
96113658Sdeischen				 */
97113658Sdeischen				pthread->cancelflags |= THR_CANCELLING;
98113658Sdeischen				pthread->critical_yield = 1;
99113658Sdeischen				break;
100113658Sdeischen
10153812Salfred			case PS_SLEEP_WAIT:
102111035Smini			case PS_SIGSUSPEND:
103111035Smini			case PS_SIGWAIT:
10453812Salfred				/* Interrupt and resume: */
10553812Salfred				pthread->interrupted = 1;
106113658Sdeischen				pthread->cancelflags |= THR_CANCELLING;
107117907Sdeischen				kmbx = _thr_setrunnable_unlocked(pthread);
10853812Salfred				break;
10953812Salfred
11076909Sjasone			case PS_JOIN:
111115278Sdeischen				/* Disconnect the thread from the joinee: */
112115278Sdeischen				joinee = pthread->join_status.thread;
113115278Sdeischen				pthread->join_status.thread = NULL;
114113658Sdeischen				pthread->cancelflags |= THR_CANCELLING;
115117907Sdeischen				kmbx = _thr_setrunnable_unlocked(pthread);
116115278Sdeischen				if ((joinee != NULL) &&
117117162Sdavidxu				    (pthread->kseg == joinee->kseg)) {
118115278Sdeischen					/* Remove the joiner from the joinee. */
119115278Sdeischen					joinee->joiner = NULL;
120115278Sdeischen					joinee = NULL;
121115278Sdeischen				}
12281750Sjasone				break;
12381750Sjasone
12461681Sjasone			case PS_SUSPENDED:
12553812Salfred			case PS_MUTEX_WAIT:
12653812Salfred			case PS_COND_WAIT:
12753812Salfred				/*
12853812Salfred				 * Threads in these states may be in queues.
12953812Salfred				 * In order to preserve queue integrity, the
13053812Salfred				 * cancelled thread must remove itself from the
13153812Salfred				 * queue.  Mark the thread as interrupted and
13253812Salfred				 * needing cancellation, and set the state to
13353812Salfred				 * running.  When the thread resumes, it will
13456277Sjasone				 * remove itself from the queue and call the
13556277Sjasone				 * cancellation completion routine.
13653812Salfred				 */
13753812Salfred				pthread->interrupted = 1;
138113658Sdeischen				pthread->cancelflags |= THR_CANCEL_NEEDED;
139117907Sdeischen				kmbx = _thr_setrunnable_unlocked(pthread);
140120895Sdavidxu				pthread->continuation =
141120895Sdavidxu					_thr_finish_cancellation;
14253812Salfred				break;
14353812Salfred
14453812Salfred			case PS_DEAD:
14553812Salfred			case PS_DEADLOCK:
14653812Salfred			case PS_STATE_MAX:
14753812Salfred				/* Ignore - only here to silence -Wall: */
14853812Salfred				break;
14954708Sdeischen			}
150117706Sdavidxu			if ((pthread->cancelflags & THR_AT_CANCEL_POINT) &&
151117706Sdavidxu			    (pthread->blocked != 0 ||
152117706Sdavidxu			     pthread->attr.flags & PTHREAD_SCOPE_SYSTEM))
153118510Sdeischen				kse_thr_interrupt(&pthread->tcb->tcb_tmbx,
154117706Sdavidxu					KSE_INTR_INTERRUPT, 0);
15553812Salfred		}
15654708Sdeischen
157113658Sdeischen		/*
158120897Sdavidxu		 * Release the thread's lock and remove the
159113658Sdeischen		 * reference:
160113658Sdeischen		 */
161113658Sdeischen		THR_SCHED_UNLOCK(curthread, pthread);
162120895Sdavidxu		THR_THREAD_UNLOCK(curthread, pthread);
163113658Sdeischen		_thr_ref_delete(curthread, pthread);
164117907Sdeischen		if (kmbx != NULL)
165117907Sdeischen			kse_wakeup(kmbx);
166115278Sdeischen
167115278Sdeischen		if ((joinee != NULL) &&
168115278Sdeischen		    (_thr_ref_add(curthread, joinee, /* include dead */1) == 0)) {
169115278Sdeischen			/* Remove the joiner from the joinee. */
170115278Sdeischen			THR_SCHED_LOCK(curthread, joinee);
171115278Sdeischen			joinee->joiner = NULL;
172115278Sdeischen			THR_SCHED_UNLOCK(curthread, joinee);
173115278Sdeischen			_thr_ref_delete(curthread, joinee);
174115278Sdeischen		}
17553812Salfred	}
17653812Salfred	return (ret);
17753812Salfred}
17853812Salfred
17953812Salfredint
18071581Sdeischen_pthread_setcancelstate(int state, int *oldstate)
18153812Salfred{
18271581Sdeischen	struct pthread	*curthread = _get_curthread();
18353812Salfred	int ostate;
18453812Salfred	int ret;
185113658Sdeischen	int need_exit = 0;
18653812Salfred
187120897Sdavidxu	/* Take the thread's lock while fiddling with the state: */
188120895Sdavidxu	THR_THREAD_LOCK(curthread, curthread);
189113658Sdeischen
19071581Sdeischen	ostate = curthread->cancelflags & PTHREAD_CANCEL_DISABLE;
19153812Salfred
19253812Salfred	switch (state) {
19353812Salfred	case PTHREAD_CANCEL_ENABLE:
19471581Sdeischen		curthread->cancelflags &= ~PTHREAD_CANCEL_DISABLE;
19571581Sdeischen		if ((curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0)
196113658Sdeischen			need_exit = checkcancel(curthread);
19753812Salfred		ret = 0;
19853812Salfred		break;
19953812Salfred	case PTHREAD_CANCEL_DISABLE:
20071581Sdeischen		curthread->cancelflags |= PTHREAD_CANCEL_DISABLE;
20153812Salfred		ret = 0;
20253812Salfred		break;
20353812Salfred	default:
20453812Salfred		ret = EINVAL;
20553812Salfred	}
20653812Salfred
207120895Sdavidxu	THR_THREAD_UNLOCK(curthread, curthread);
208113658Sdeischen	if (need_exit != 0) {
209113658Sdeischen		_thr_exit_cleanup();
210174112Sdeischen		_pthread_exit(PTHREAD_CANCELED);
211113658Sdeischen		PANIC("cancel");
212113658Sdeischen	}
213117300Sdavidxu	if (ret == 0 && oldstate != NULL)
214117300Sdavidxu		*oldstate = ostate;
215117300Sdavidxu
21653812Salfred	return (ret);
21753812Salfred}
21853812Salfred
21953812Salfredint
22071581Sdeischen_pthread_setcanceltype(int type, int *oldtype)
22153812Salfred{
22271581Sdeischen	struct pthread	*curthread = _get_curthread();
22353812Salfred	int otype;
22453812Salfred	int ret;
225113658Sdeischen	int need_exit = 0;
22653812Salfred
227120897Sdavidxu	/* Take the thread's lock while fiddling with the state: */
228120895Sdavidxu	THR_THREAD_LOCK(curthread, curthread);
229113658Sdeischen
23071581Sdeischen	otype = curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS;
23153812Salfred	switch (type) {
23253812Salfred	case PTHREAD_CANCEL_ASYNCHRONOUS:
23371581Sdeischen		curthread->cancelflags |= PTHREAD_CANCEL_ASYNCHRONOUS;
234113658Sdeischen		need_exit = checkcancel(curthread);
23553812Salfred		ret = 0;
23653812Salfred		break;
23753812Salfred	case PTHREAD_CANCEL_DEFERRED:
23871581Sdeischen		curthread->cancelflags &= ~PTHREAD_CANCEL_ASYNCHRONOUS;
23953812Salfred		ret = 0;
24053812Salfred		break;
24153812Salfred	default:
24253812Salfred		ret = EINVAL;
24353812Salfred	}
24453812Salfred
245120895Sdavidxu	THR_THREAD_UNLOCK(curthread, curthread);
246113658Sdeischen	if (need_exit != 0) {
247113658Sdeischen		_thr_exit_cleanup();
248174112Sdeischen		_pthread_exit(PTHREAD_CANCELED);
249113658Sdeischen		PANIC("cancel");
250113658Sdeischen	}
251117300Sdavidxu	if (ret == 0 && oldtype != NULL)
252117300Sdavidxu		*oldtype = otype;
253117300Sdavidxu
25453812Salfred	return (ret);
25553812Salfred}
25653812Salfred
25753812Salfredvoid
258113658Sdeischen_pthread_testcancel(void)
25953812Salfred{
26071581Sdeischen	struct pthread	*curthread = _get_curthread();
26171581Sdeischen
262120895Sdavidxu	THR_THREAD_LOCK(curthread, curthread);
263113658Sdeischen	testcancel(curthread);
264120895Sdavidxu	THR_THREAD_UNLOCK(curthread, curthread);
265113658Sdeischen}
266113658Sdeischen
267113658Sdeischenvoid
268123312Sdavidxu_thr_cancel_enter(struct pthread *thread)
269113658Sdeischen{
27053812Salfred	/* Look for a cancellation before we block: */
271120895Sdavidxu	THR_THREAD_LOCK(thread, thread);
272113658Sdeischen	testcancel(thread);
273113658Sdeischen	thread->cancelflags |= THR_AT_CANCEL_POINT;
274120895Sdavidxu	THR_THREAD_UNLOCK(thread, thread);
27553812Salfred}
27653812Salfred
27753812Salfredvoid
278123312Sdavidxu_thr_cancel_leave(struct pthread *thread, int check)
27953812Salfred{
280120895Sdavidxu	THR_THREAD_LOCK(thread, thread);
281113658Sdeischen	thread->cancelflags &= ~THR_AT_CANCEL_POINT;
28253812Salfred	/* Look for a cancellation after we unblock: */
283123312Sdavidxu	if (check)
284123312Sdavidxu		testcancel(thread);
285120895Sdavidxu	THR_THREAD_UNLOCK(thread, thread);
28653812Salfred}
28756277Sjasone
288120895Sdavidxuvoid
289174112Sdeischen_thr_finish_cancellation(void *arg __unused)
29056277Sjasone{
29171581Sdeischen	struct pthread	*curthread = _get_curthread();
29256277Sjasone
29371581Sdeischen	curthread->continuation = NULL;
29471581Sdeischen	curthread->interrupted = 0;
29571581Sdeischen
296120895Sdavidxu	THR_THREAD_LOCK(curthread, curthread);
297113658Sdeischen	if ((curthread->cancelflags & THR_CANCEL_NEEDED) != 0) {
298113658Sdeischen		curthread->cancelflags &= ~THR_CANCEL_NEEDED;
299120895Sdavidxu		THR_THREAD_UNLOCK(curthread, curthread);
300113658Sdeischen		_thr_exit_cleanup();
301174112Sdeischen		_pthread_exit(PTHREAD_CANCELED);
30256277Sjasone	}
303120895Sdavidxu	THR_THREAD_UNLOCK(curthread, curthread);
30456277Sjasone}
305