thr_cancel.c revision 123312
1/*
2 * David Leonard <d@openbsd.org>, 1999. Public domain.
3 * $FreeBSD: head/lib/libkse/thread/thr_cancel.c 123312 2003-12-09 02:20:56Z davidxu $
4 */
5#include <sys/errno.h>
6#include <pthread.h>
7#include "thr_private.h"
8
9__weak_reference(_pthread_cancel, pthread_cancel);
10__weak_reference(_pthread_setcancelstate, pthread_setcancelstate);
11__weak_reference(_pthread_setcanceltype, pthread_setcanceltype);
12__weak_reference(_pthread_testcancel, pthread_testcancel);
13
14static inline int
15checkcancel(struct pthread *curthread)
16{
17	if (((curthread->cancelflags & PTHREAD_CANCEL_DISABLE) == 0) &&
18	    ((curthread->cancelflags & THR_CANCELLING) != 0)) {
19		/*
20		 * It is possible for this thread to be swapped out
21		 * while performing cancellation; do not allow it
22		 * to be cancelled again.
23		 */
24		curthread->cancelflags &= ~THR_CANCELLING;
25		return (1);
26	}
27	else
28		return (0);
29}
30
31static inline void
32testcancel(struct pthread *curthread)
33{
34	if (checkcancel(curthread) != 0) {
35		/* Unlock before exiting: */
36		THR_THREAD_UNLOCK(curthread, curthread);
37
38		_thr_exit_cleanup();
39		pthread_exit(PTHREAD_CANCELED);
40		PANIC("cancel");
41	}
42}
43
44int
45_pthread_cancel(pthread_t pthread)
46{
47	struct pthread *curthread = _get_curthread();
48	struct pthread *joinee = NULL;
49	struct kse_mailbox *kmbx = NULL;
50	int ret;
51
52	if ((ret = _thr_ref_add(curthread, pthread, /*include dead*/0)) == 0) {
53		/*
54		 * Take the thread's lock while we change the cancel flags.
55		 */
56		THR_THREAD_LOCK(curthread, pthread);
57		THR_SCHED_LOCK(curthread, pthread);
58		if (pthread->flags & THR_FLAGS_EXITING) {
59			THR_SCHED_UNLOCK(curthread, pthread);
60			THR_THREAD_UNLOCK(curthread, pthread);
61			_thr_ref_delete(curthread, pthread);
62			return (ESRCH);
63		}
64		if (((pthread->cancelflags & PTHREAD_CANCEL_DISABLE) != 0) ||
65		    (((pthread->cancelflags & THR_AT_CANCEL_POINT) == 0) &&
66		    ((pthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) == 0)))
67			/* Just mark it for cancellation: */
68			pthread->cancelflags |= THR_CANCELLING;
69		else {
70			/*
71			 * Check if we need to kick it back into the
72			 * run queue:
73			 */
74			switch (pthread->state) {
75			case PS_RUNNING:
76				/* No need to resume: */
77				pthread->cancelflags |= THR_CANCELLING;
78				break;
79
80			case PS_LOCKWAIT:
81				/*
82				 * These can't be removed from the queue.
83				 * Just mark it as cancelling and tell it
84				 * to yield once it leaves the critical
85				 * region.
86				 */
87				pthread->cancelflags |= THR_CANCELLING;
88				pthread->critical_yield = 1;
89				break;
90
91			case PS_SLEEP_WAIT:
92			case PS_SIGSUSPEND:
93			case PS_SIGWAIT:
94				/* Interrupt and resume: */
95				pthread->interrupted = 1;
96				pthread->cancelflags |= THR_CANCELLING;
97				kmbx = _thr_setrunnable_unlocked(pthread);
98				break;
99
100			case PS_JOIN:
101				/* Disconnect the thread from the joinee: */
102				joinee = pthread->join_status.thread;
103				pthread->join_status.thread = NULL;
104				pthread->cancelflags |= THR_CANCELLING;
105				kmbx = _thr_setrunnable_unlocked(pthread);
106				if ((joinee != NULL) &&
107				    (pthread->kseg == joinee->kseg)) {
108					/* Remove the joiner from the joinee. */
109					joinee->joiner = NULL;
110					joinee = NULL;
111				}
112				break;
113
114			case PS_SUSPENDED:
115			case PS_MUTEX_WAIT:
116			case PS_COND_WAIT:
117				/*
118				 * Threads in these states may be in queues.
119				 * In order to preserve queue integrity, the
120				 * cancelled thread must remove itself from the
121				 * queue.  Mark the thread as interrupted and
122				 * needing cancellation, and set the state to
123				 * running.  When the thread resumes, it will
124				 * remove itself from the queue and call the
125				 * cancellation completion routine.
126				 */
127				pthread->interrupted = 1;
128				pthread->cancelflags |= THR_CANCEL_NEEDED;
129				kmbx = _thr_setrunnable_unlocked(pthread);
130				pthread->continuation =
131					_thr_finish_cancellation;
132				break;
133
134			case PS_DEAD:
135			case PS_DEADLOCK:
136			case PS_STATE_MAX:
137				/* Ignore - only here to silence -Wall: */
138				break;
139			}
140			if ((pthread->cancelflags & THR_AT_CANCEL_POINT) &&
141			    (pthread->blocked != 0 ||
142			     pthread->attr.flags & PTHREAD_SCOPE_SYSTEM))
143				kse_thr_interrupt(&pthread->tcb->tcb_tmbx,
144					KSE_INTR_INTERRUPT, 0);
145		}
146
147		/*
148		 * Release the thread's lock and remove the
149		 * reference:
150		 */
151		THR_SCHED_UNLOCK(curthread, pthread);
152		THR_THREAD_UNLOCK(curthread, pthread);
153		_thr_ref_delete(curthread, pthread);
154		if (kmbx != NULL)
155			kse_wakeup(kmbx);
156
157		if ((joinee != NULL) &&
158		    (_thr_ref_add(curthread, joinee, /* include dead */1) == 0)) {
159			/* Remove the joiner from the joinee. */
160			THR_SCHED_LOCK(curthread, joinee);
161			joinee->joiner = NULL;
162			THR_SCHED_UNLOCK(curthread, joinee);
163			_thr_ref_delete(curthread, joinee);
164		}
165	}
166	return (ret);
167}
168
169int
170_pthread_setcancelstate(int state, int *oldstate)
171{
172	struct pthread	*curthread = _get_curthread();
173	int ostate;
174	int ret;
175	int need_exit = 0;
176
177	/* Take the thread's lock while fiddling with the state: */
178	THR_THREAD_LOCK(curthread, curthread);
179
180	ostate = curthread->cancelflags & PTHREAD_CANCEL_DISABLE;
181
182	switch (state) {
183	case PTHREAD_CANCEL_ENABLE:
184		curthread->cancelflags &= ~PTHREAD_CANCEL_DISABLE;
185		if ((curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0)
186			need_exit = checkcancel(curthread);
187		ret = 0;
188		break;
189	case PTHREAD_CANCEL_DISABLE:
190		curthread->cancelflags |= PTHREAD_CANCEL_DISABLE;
191		ret = 0;
192		break;
193	default:
194		ret = EINVAL;
195	}
196
197	THR_THREAD_UNLOCK(curthread, curthread);
198	if (need_exit != 0) {
199		_thr_exit_cleanup();
200		pthread_exit(PTHREAD_CANCELED);
201		PANIC("cancel");
202	}
203	if (ret == 0 && oldstate != NULL)
204		*oldstate = ostate;
205
206	return (ret);
207}
208
209int
210_pthread_setcanceltype(int type, int *oldtype)
211{
212	struct pthread	*curthread = _get_curthread();
213	int otype;
214	int ret;
215	int need_exit = 0;
216
217	/* Take the thread's lock while fiddling with the state: */
218	THR_THREAD_LOCK(curthread, curthread);
219
220	otype = curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS;
221	switch (type) {
222	case PTHREAD_CANCEL_ASYNCHRONOUS:
223		curthread->cancelflags |= PTHREAD_CANCEL_ASYNCHRONOUS;
224		need_exit = checkcancel(curthread);
225		ret = 0;
226		break;
227	case PTHREAD_CANCEL_DEFERRED:
228		curthread->cancelflags &= ~PTHREAD_CANCEL_ASYNCHRONOUS;
229		ret = 0;
230		break;
231	default:
232		ret = EINVAL;
233	}
234
235	THR_THREAD_UNLOCK(curthread, curthread);
236	if (need_exit != 0) {
237		_thr_exit_cleanup();
238		pthread_exit(PTHREAD_CANCELED);
239		PANIC("cancel");
240	}
241	if (ret == 0 && oldtype != NULL)
242		*oldtype = otype;
243
244	return (ret);
245}
246
247void
248_pthread_testcancel(void)
249{
250	struct pthread	*curthread = _get_curthread();
251
252	THR_THREAD_LOCK(curthread, curthread);
253	testcancel(curthread);
254	THR_THREAD_UNLOCK(curthread, curthread);
255}
256
257void
258_thr_cancel_enter(struct pthread *thread)
259{
260	/* Look for a cancellation before we block: */
261	THR_THREAD_LOCK(thread, thread);
262	testcancel(thread);
263	thread->cancelflags |= THR_AT_CANCEL_POINT;
264	THR_THREAD_UNLOCK(thread, thread);
265}
266
267void
268_thr_cancel_leave(struct pthread *thread, int check)
269{
270	THR_THREAD_LOCK(thread, thread);
271	thread->cancelflags &= ~THR_AT_CANCEL_POINT;
272	/* Look for a cancellation after we unblock: */
273	if (check)
274		testcancel(thread);
275	THR_THREAD_UNLOCK(thread, thread);
276}
277
278void
279_thr_finish_cancellation(void *arg)
280{
281	struct pthread	*curthread = _get_curthread();
282
283	curthread->continuation = NULL;
284	curthread->interrupted = 0;
285
286	THR_THREAD_LOCK(curthread, curthread);
287	if ((curthread->cancelflags & THR_CANCEL_NEEDED) != 0) {
288		curthread->cancelflags &= ~THR_CANCEL_NEEDED;
289		THR_THREAD_UNLOCK(curthread, curthread);
290		_thr_exit_cleanup();
291		pthread_exit(PTHREAD_CANCELED);
292	}
293	THR_THREAD_UNLOCK(curthread, curthread);
294}
295