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