thr_cancel.c revision 75369
1/*
2 * David Leonard <d@openbsd.org>, 1999. Public domain.
3 * $FreeBSD: head/lib/libkse/thread/thr_cancel.c 75369 2001-04-10 04:19:21Z deischen $
4 */
5#include <sys/errno.h>
6#include <pthread.h>
7#include "pthread_private.h"
8
9static void	finish_cancellation(void *arg);
10
11__weak_reference(_pthread_cancel, pthread_cancel);
12__weak_reference(_pthread_setcancelstate, pthread_setcancelstate);
13__weak_reference(_pthread_setcanceltype, pthread_setcanceltype);
14__weak_reference(_pthread_testcancel, pthread_testcancel);
15
16int
17_pthread_cancel(pthread_t pthread)
18{
19	int ret;
20
21	if ((ret = _find_thread(pthread)) != 0) {
22		/* NOTHING */
23	} else if (pthread->state == PS_DEAD || pthread->state == PS_DEADLOCK) {
24		ret = 0;
25	} else {
26		/* Protect the scheduling queues: */
27		_thread_kern_sig_defer();
28
29		if (((pthread->cancelflags & PTHREAD_CANCEL_DISABLE) != 0) ||
30		    (((pthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) == 0) &&
31		    ((pthread->cancelflags & PTHREAD_AT_CANCEL_POINT) == 0)))
32			/* Just mark it for cancellation: */
33			pthread->cancelflags |= PTHREAD_CANCELLING;
34		else {
35			/*
36			 * Check if we need to kick it back into the
37			 * run queue:
38			 */
39			switch (pthread->state) {
40			case PS_RUNNING:
41				/* No need to resume: */
42				pthread->cancelflags |= PTHREAD_CANCELLING;
43				break;
44
45			case PS_SPINBLOCK:
46			case PS_FDR_WAIT:
47			case PS_FDW_WAIT:
48			case PS_POLL_WAIT:
49			case PS_SELECT_WAIT:
50				/* Remove these threads from the work queue: */
51				if ((pthread->flags & PTHREAD_FLAGS_IN_WORKQ)
52				    != 0)
53					PTHREAD_WORKQ_REMOVE(pthread);
54				/* Fall through: */
55			case PS_SIGTHREAD:
56			case PS_SLEEP_WAIT:
57			case PS_WAIT_WAIT:
58			case PS_SIGSUSPEND:
59			case PS_SIGWAIT:
60				/* Interrupt and resume: */
61				pthread->interrupted = 1;
62				pthread->cancelflags |= PTHREAD_CANCELLING;
63				PTHREAD_NEW_STATE(pthread,PS_RUNNING);
64				break;
65
66			case PS_SUSPENDED:
67				if (pthread->suspended == SUSP_NO ||
68				    pthread->suspended == SUSP_YES ||
69				    pthread->suspended == SUSP_NOWAIT) {
70					/*
71					 * This thread isn't in any scheduling
72					 * queues; just change it's state:
73					 */
74					pthread->cancelflags |=
75					    PTHREAD_CANCELLING;
76					PTHREAD_SET_STATE(pthread, PS_RUNNING);
77					break;
78				}
79				/* FALLTHROUGH */
80			case PS_MUTEX_WAIT:
81			case PS_COND_WAIT:
82			case PS_FDLR_WAIT:
83			case PS_FDLW_WAIT:
84			case PS_FILE_WAIT:
85			case PS_JOIN:
86				/*
87				 * Threads in these states may be in queues.
88				 * In order to preserve queue integrity, the
89				 * cancelled thread must remove itself from the
90				 * queue.  Mark the thread as interrupted and
91				 * needing cancellation, and set the state to
92				 * running.  When the thread resumes, it will
93				 * remove itself from the queue and call the
94				 * cancellation completion routine.
95				 */
96				pthread->interrupted = 1;
97				pthread->cancelflags |= PTHREAD_CANCEL_NEEDED;
98				PTHREAD_NEW_STATE(pthread,PS_RUNNING);
99				pthread->continuation = finish_cancellation;
100				break;
101
102			case PS_DEAD:
103			case PS_DEADLOCK:
104			case PS_STATE_MAX:
105				/* Ignore - only here to silence -Wall: */
106				break;
107			}
108		}
109
110		/* Unprotect the scheduling queues: */
111		_thread_kern_sig_undefer();
112
113		ret = 0;
114	}
115	return (ret);
116}
117
118int
119_pthread_setcancelstate(int state, int *oldstate)
120{
121	struct pthread	*curthread = _get_curthread();
122	int ostate;
123	int ret;
124
125	ostate = curthread->cancelflags & PTHREAD_CANCEL_DISABLE;
126
127	switch (state) {
128	case PTHREAD_CANCEL_ENABLE:
129		if (oldstate != NULL)
130			*oldstate = ostate;
131		curthread->cancelflags &= ~PTHREAD_CANCEL_DISABLE;
132		if ((curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0)
133			pthread_testcancel();
134		ret = 0;
135		break;
136	case PTHREAD_CANCEL_DISABLE:
137		if (oldstate != NULL)
138			*oldstate = ostate;
139		curthread->cancelflags |= PTHREAD_CANCEL_DISABLE;
140		ret = 0;
141		break;
142	default:
143		ret = EINVAL;
144	}
145
146	return (ret);
147}
148
149int
150_pthread_setcanceltype(int type, int *oldtype)
151{
152	struct pthread	*curthread = _get_curthread();
153	int otype;
154	int ret;
155
156	otype = curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS;
157	switch (type) {
158	case PTHREAD_CANCEL_ASYNCHRONOUS:
159		if (oldtype != NULL)
160			*oldtype = otype;
161		curthread->cancelflags |= PTHREAD_CANCEL_ASYNCHRONOUS;
162		pthread_testcancel();
163		ret = 0;
164		break;
165	case PTHREAD_CANCEL_DEFERRED:
166		if (oldtype != NULL)
167			*oldtype = otype;
168		curthread->cancelflags &= ~PTHREAD_CANCEL_ASYNCHRONOUS;
169		ret = 0;
170		break;
171	default:
172		ret = EINVAL;
173	}
174
175	return (ret);
176}
177
178void
179_pthread_testcancel(void)
180{
181	struct pthread	*curthread = _get_curthread();
182
183	if (((curthread->cancelflags & PTHREAD_CANCEL_DISABLE) == 0) &&
184	    ((curthread->cancelflags & PTHREAD_CANCELLING) != 0)) {
185		/*
186		 * It is possible for this thread to be swapped out
187		 * while performing cancellation; do not allow it
188		 * to be cancelled again.
189		 */
190		curthread->cancelflags &= ~PTHREAD_CANCELLING;
191		_thread_exit_cleanup();
192		pthread_exit(PTHREAD_CANCELED);
193		PANIC("cancel");
194	}
195}
196
197void
198_thread_enter_cancellation_point(void)
199{
200	struct pthread	*curthread = _get_curthread();
201
202	/* Look for a cancellation before we block: */
203	pthread_testcancel();
204	curthread->cancelflags |= PTHREAD_AT_CANCEL_POINT;
205}
206
207void
208_thread_leave_cancellation_point(void)
209{
210	struct pthread	*curthread = _get_curthread();
211
212	curthread->cancelflags &= ~PTHREAD_AT_CANCEL_POINT;
213	/* Look for a cancellation after we unblock: */
214	pthread_testcancel();
215}
216
217static void
218finish_cancellation(void *arg)
219{
220	struct pthread	*curthread = _get_curthread();
221
222	curthread->continuation = NULL;
223	curthread->interrupted = 0;
224
225	if ((curthread->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) {
226		curthread->cancelflags &= ~PTHREAD_CANCEL_NEEDED;
227		_thread_exit_cleanup();
228		pthread_exit(PTHREAD_CANCELED);
229	}
230}
231