thr_cancel.c revision 76909
1/*
2 * David Leonard <d@openbsd.org>, 1999. Public domain.
3 * $FreeBSD: head/lib/libkse/thread/thr_cancel.c 76909 2001-05-20 23:08:33Z jasone $
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_JOIN:
67			case PS_SUSPENDED:
68				if (pthread->suspended == SUSP_NO ||
69				    pthread->suspended == SUSP_YES ||
70				    pthread->suspended == SUSP_NOWAIT) {
71					/*
72					 * This thread isn't in any scheduling
73					 * queues; just change it's state:
74					 */
75					pthread->cancelflags |=
76					    PTHREAD_CANCELLING;
77					PTHREAD_SET_STATE(pthread, PS_RUNNING);
78					break;
79				}
80				/* FALLTHROUGH */
81			case PS_MUTEX_WAIT:
82			case PS_COND_WAIT:
83			case PS_FDLR_WAIT:
84			case PS_FDLW_WAIT:
85			case PS_FILE_WAIT:
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_detach((pthread_t)curthread);
193		pthread_exit(PTHREAD_CANCELED);
194		PANIC("cancel");
195	}
196}
197
198void
199_thread_enter_cancellation_point(void)
200{
201	struct pthread	*curthread = _get_curthread();
202
203	/* Look for a cancellation before we block: */
204	pthread_testcancel();
205	curthread->cancelflags |= PTHREAD_AT_CANCEL_POINT;
206}
207
208void
209_thread_leave_cancellation_point(void)
210{
211	struct pthread	*curthread = _get_curthread();
212
213	curthread->cancelflags &= ~PTHREAD_AT_CANCEL_POINT;
214	/* Look for a cancellation after we unblock: */
215	pthread_testcancel();
216}
217
218static void
219finish_cancellation(void *arg)
220{
221	struct pthread	*curthread = _get_curthread();
222
223	curthread->continuation = NULL;
224	curthread->interrupted = 0;
225
226	if ((curthread->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) {
227		curthread->cancelflags &= ~PTHREAD_CANCEL_NEEDED;
228		_thread_exit_cleanup();
229		pthread_detach((pthread_t)curthread);
230		pthread_exit(PTHREAD_CANCELED);
231	}
232}
233