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