thr_cancel.c revision 111035
1/*
2 * David Leonard <d@openbsd.org>, 1999. Public domain.
3 * $FreeBSD: head/lib/libkse/thread/thr_cancel.c 111035 2003-02-17 10:05:18Z mini $
4 */
5#include <sys/errno.h>
6#include <pthread.h>
7#include "thr_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				/* Remove these threads from the work queue: */
48				if ((pthread->flags & PTHREAD_FLAGS_IN_WORKQ)
49				    != 0)
50					PTHREAD_WORKQ_REMOVE(pthread);
51				/* Fall through: */
52			case PS_SLEEP_WAIT:
53			case PS_WAIT_WAIT:
54			case PS_SIGSUSPEND:
55			case PS_SIGWAIT:
56				/* Interrupt and resume: */
57				pthread->interrupted = 1;
58				pthread->cancelflags |= PTHREAD_CANCELLING;
59				PTHREAD_NEW_STATE(pthread,PS_RUNNING);
60				break;
61
62			case PS_JOIN:
63				/*
64				 * Disconnect the thread from the joinee:
65				 */
66				if (pthread->join_status.thread != NULL) {
67					pthread->join_status.thread->joiner
68					    = NULL;
69					pthread->join_status.thread = NULL;
70				}
71				pthread->cancelflags |= PTHREAD_CANCELLING;
72				PTHREAD_NEW_STATE(pthread, PS_RUNNING);
73				break;
74
75			case PS_SUSPENDED:
76			case PS_MUTEX_WAIT:
77			case PS_COND_WAIT:
78				/*
79				 * Threads in these states may be in queues.
80				 * In order to preserve queue integrity, the
81				 * cancelled thread must remove itself from the
82				 * queue.  Mark the thread as interrupted and
83				 * needing cancellation, and set the state to
84				 * running.  When the thread resumes, it will
85				 * remove itself from the queue and call the
86				 * cancellation completion routine.
87				 */
88				pthread->interrupted = 1;
89				pthread->cancelflags |= PTHREAD_CANCEL_NEEDED;
90				PTHREAD_NEW_STATE(pthread, PS_RUNNING);
91				pthread->continuation = finish_cancellation;
92				break;
93
94			case PS_DEAD:
95			case PS_DEADLOCK:
96			case PS_STATE_MAX:
97				/* Ignore - only here to silence -Wall: */
98				break;
99			}
100		}
101
102		/* Unprotect the scheduling queues: */
103		_thread_kern_sig_undefer();
104
105		ret = 0;
106	}
107	return (ret);
108}
109
110int
111_pthread_setcancelstate(int state, int *oldstate)
112{
113	struct pthread	*curthread = _get_curthread();
114	int ostate;
115	int ret;
116
117	ostate = curthread->cancelflags & PTHREAD_CANCEL_DISABLE;
118
119	switch (state) {
120	case PTHREAD_CANCEL_ENABLE:
121		if (oldstate != NULL)
122			*oldstate = ostate;
123		curthread->cancelflags &= ~PTHREAD_CANCEL_DISABLE;
124		if ((curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0)
125			pthread_testcancel();
126		ret = 0;
127		break;
128	case PTHREAD_CANCEL_DISABLE:
129		if (oldstate != NULL)
130			*oldstate = ostate;
131		curthread->cancelflags |= PTHREAD_CANCEL_DISABLE;
132		ret = 0;
133		break;
134	default:
135		ret = EINVAL;
136	}
137
138	return (ret);
139}
140
141int
142_pthread_setcanceltype(int type, int *oldtype)
143{
144	struct pthread	*curthread = _get_curthread();
145	int otype;
146	int ret;
147
148	otype = curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS;
149	switch (type) {
150	case PTHREAD_CANCEL_ASYNCHRONOUS:
151		if (oldtype != NULL)
152			*oldtype = otype;
153		curthread->cancelflags |= PTHREAD_CANCEL_ASYNCHRONOUS;
154		pthread_testcancel();
155		ret = 0;
156		break;
157	case PTHREAD_CANCEL_DEFERRED:
158		if (oldtype != NULL)
159			*oldtype = otype;
160		curthread->cancelflags &= ~PTHREAD_CANCEL_ASYNCHRONOUS;
161		ret = 0;
162		break;
163	default:
164		ret = EINVAL;
165	}
166
167	return (ret);
168}
169
170void
171_pthread_testcancel(void)
172{
173	struct pthread	*curthread = _get_curthread();
174
175	if (((curthread->cancelflags & PTHREAD_CANCEL_DISABLE) == 0) &&
176	    ((curthread->cancelflags & PTHREAD_CANCELLING) != 0) &&
177	    ((curthread->flags & PTHREAD_EXITING) == 0)) {
178		/*
179		 * It is possible for this thread to be swapped out
180		 * while performing cancellation; do not allow it
181		 * to be cancelled again.
182		 */
183		curthread->cancelflags &= ~PTHREAD_CANCELLING;
184		_thread_exit_cleanup();
185		pthread_exit(PTHREAD_CANCELED);
186		PANIC("cancel");
187	}
188}
189
190void
191_thread_enter_cancellation_point(void)
192{
193	struct pthread	*curthread = _get_curthread();
194
195	/* Look for a cancellation before we block: */
196	pthread_testcancel();
197	curthread->cancelflags |= PTHREAD_AT_CANCEL_POINT;
198}
199
200void
201_thread_leave_cancellation_point(void)
202{
203	struct pthread	*curthread = _get_curthread();
204
205	curthread->cancelflags &= ~PTHREAD_AT_CANCEL_POINT;
206	/* Look for a cancellation after we unblock: */
207	pthread_testcancel();
208}
209
210static void
211finish_cancellation(void *arg)
212{
213	struct pthread	*curthread = _get_curthread();
214
215	curthread->continuation = NULL;
216	curthread->interrupted = 0;
217
218	if ((curthread->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) {
219		curthread->cancelflags &= ~PTHREAD_CANCEL_NEEDED;
220		_thread_exit_cleanup();
221		pthread_exit(PTHREAD_CANCELED);
222	}
223}
224