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