xref: /illumos-gate/usr/src/lib/libc/port/threads/cancel.c (revision 4a38094c)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include "lint.h"
28 #include "thr_uberdata.h"
29 
30 /*
31  * pthread_cancel: tries to cancel the targeted thread.
32  * If the target thread has already exited no action is taken.
33  * Else send SIGCANCEL to request the other thread to cancel itself.
34  */
35 int
pthread_cancel(thread_t tid)36 pthread_cancel(thread_t tid)
37 {
38 	ulwp_t *self = curthread;
39 	uberdata_t *udp = self->ul_uberdata;
40 	ulwp_t *ulwp;
41 	int error = 0;
42 
43 	if ((ulwp = find_lwp(tid)) == NULL)
44 		return (ESRCH);
45 
46 	if (ulwp->ul_cancel_pending) {
47 		/*
48 		 * Don't send SIGCANCEL more than once.
49 		 */
50 		ulwp_unlock(ulwp, udp);
51 	} else if (ulwp == self) {
52 		/*
53 		 * Unlock self before cancelling.
54 		 */
55 		ulwp_unlock(self, udp);
56 		self->ul_nocancel = 0;	/* cancellation is now possible */
57 		if (self->ul_sigdefer == 0)
58 			do_sigcancel();
59 		else {
60 			self->ul_cancel_pending = 1;
61 			set_cancel_pending_flag(self, 0);
62 		}
63 	} else if (ulwp->ul_cancel_disabled) {
64 		/*
65 		 * Don't send SIGCANCEL if cancellation is disabled;
66 		 * just set the thread's ulwp->ul_cancel_pending flag.
67 		 * This avoids a potential EINTR for the target thread.
68 		 * We don't call set_cancel_pending_flag() here because
69 		 * we cannot modify another thread's schedctl data.
70 		 */
71 		ulwp->ul_cancel_pending = 1;
72 		ulwp_unlock(ulwp, udp);
73 	} else {
74 		/*
75 		 * Request the other thread to cancel itself.
76 		 */
77 		error = _lwp_kill(tid, SIGCANCEL);
78 		ulwp_unlock(ulwp, udp);
79 	}
80 
81 	return (error);
82 }
83 
84 /*
85  * pthread_setcancelstate: sets the state ENABLED or DISABLED.
86  * If the state is already ENABLED or is being set to ENABLED,
87  * the type of cancellation is ASYNCHRONOUS, and a cancel request
88  * is pending, then the thread is cancelled right here.
89  * Otherwise, pthread_setcancelstate() is not a cancellation point.
90  */
91 int
pthread_setcancelstate(int state,int * oldstate)92 pthread_setcancelstate(int state, int *oldstate)
93 {
94 	ulwp_t *self = curthread;
95 	uberdata_t *udp = self->ul_uberdata;
96 	int was_disabled;
97 
98 	/*
99 	 * Grab ulwp_lock(self) to protect the setting of ul_cancel_disabled
100 	 * since it is tested under this lock by pthread_cancel(), above.
101 	 * This has the side-effect of calling enter_critical() and this
102 	 * defers SIGCANCEL until ulwp_unlock(self) when exit_critical()
103 	 * is called.  (self->ul_cancel_pending is set in the SIGCANCEL
104 	 * handler and we must be async-signal safe here.)
105 	 */
106 	ulwp_lock(self, udp);
107 
108 	was_disabled = self->ul_cancel_disabled;
109 	switch (state) {
110 	case PTHREAD_CANCEL_ENABLE:
111 		self->ul_cancel_disabled = 0;
112 		break;
113 	case PTHREAD_CANCEL_DISABLE:
114 		self->ul_cancel_disabled = 1;
115 		break;
116 	default:
117 		ulwp_unlock(self, udp);
118 		return (EINVAL);
119 	}
120 	set_cancel_pending_flag(self, 0);
121 
122 	/*
123 	 * If this thread has been requested to be canceled and
124 	 * is in async mode and is or was enabled, then exit.
125 	 */
126 	if ((!self->ul_cancel_disabled || !was_disabled) &&
127 	    self->ul_cancel_async && self->ul_cancel_pending) {
128 		ulwp_unlock(self, udp);
129 		pthread_exit(PTHREAD_CANCELED);
130 	}
131 
132 	ulwp_unlock(self, udp);
133 
134 	if (oldstate != NULL) {
135 		if (was_disabled)
136 			*oldstate = PTHREAD_CANCEL_DISABLE;
137 		else
138 			*oldstate = PTHREAD_CANCEL_ENABLE;
139 	}
140 	return (0);
141 }
142 
143 /*
144  * pthread_setcanceltype: sets the type DEFERRED or ASYNCHRONOUS
145  * If the type is being set as ASYNC, then it becomes
146  * a cancellation point if there is a cancellation pending.
147  */
148 int
pthread_setcanceltype(int type,int * oldtype)149 pthread_setcanceltype(int type, int *oldtype)
150 {
151 	ulwp_t *self = curthread;
152 	int was_async;
153 
154 	/*
155 	 * Call enter_critical() to defer SIGCANCEL until exit_critical().
156 	 * We do this because curthread->ul_cancel_pending is set in the
157 	 * SIGCANCEL handler and we must be async-signal safe here.
158 	 */
159 	enter_critical(self);
160 
161 	was_async = self->ul_cancel_async;
162 	switch (type) {
163 	case PTHREAD_CANCEL_ASYNCHRONOUS:
164 		self->ul_cancel_async = 1;
165 		break;
166 	case PTHREAD_CANCEL_DEFERRED:
167 		self->ul_cancel_async = 0;
168 		break;
169 	default:
170 		exit_critical(self);
171 		return (EINVAL);
172 	}
173 	self->ul_save_async = self->ul_cancel_async;
174 
175 	/*
176 	 * If this thread has been requested to be canceled and
177 	 * is in enabled mode and is or was in async mode, exit.
178 	 */
179 	if ((self->ul_cancel_async || was_async) &&
180 	    self->ul_cancel_pending && !self->ul_cancel_disabled) {
181 		exit_critical(self);
182 		pthread_exit(PTHREAD_CANCELED);
183 	}
184 
185 	exit_critical(self);
186 
187 	if (oldtype != NULL) {
188 		if (was_async)
189 			*oldtype = PTHREAD_CANCEL_ASYNCHRONOUS;
190 		else
191 			*oldtype = PTHREAD_CANCEL_DEFERRED;
192 	}
193 	return (0);
194 }
195 
196 /*
197  * pthread_testcancel: tests for any cancellation pending
198  * if the cancellation is enabled and is pending, act on
199  * it by calling thr_exit. thr_exit takes care of calling
200  * cleanup handlers.
201  */
202 void
pthread_testcancel(void)203 pthread_testcancel(void)
204 {
205 	ulwp_t *self = curthread;
206 
207 	if (self->ul_cancel_pending && !self->ul_cancel_disabled)
208 		pthread_exit(PTHREAD_CANCELED);
209 }
210 
211 /*
212  * For deferred mode, this routine makes a thread cancelable.
213  * It is called from the functions which want to be cancellation
214  * points and are about to block, such as cond_wait().
215  */
216 void
_cancelon()217 _cancelon()
218 {
219 	ulwp_t *self = curthread;
220 
221 	ASSERT(!(self->ul_cancelable && self->ul_cancel_disabled));
222 	if (!self->ul_cancel_disabled) {
223 		ASSERT(self->ul_cancelable >= 0);
224 		self->ul_cancelable++;
225 		if (self->ul_cancel_pending)
226 			pthread_exit(PTHREAD_CANCELED);
227 	}
228 }
229 
230 /*
231  * This routine turns cancelability off and possible calls pthread_exit().
232  * It is called from functions which are cancellation points, like cond_wait().
233  */
234 void
_canceloff()235 _canceloff()
236 {
237 	ulwp_t *self = curthread;
238 
239 	ASSERT(!(self->ul_cancelable && self->ul_cancel_disabled));
240 	if (!self->ul_cancel_disabled) {
241 		if (self->ul_cancel_pending)
242 			pthread_exit(PTHREAD_CANCELED);
243 		self->ul_cancelable--;
244 		ASSERT(self->ul_cancelable >= 0);
245 	}
246 }
247 
248 /*
249  * Same as _canceloff() but don't actually cancel the thread.
250  * This is used by cond_wait() and sema_wait() when they don't get EINTR.
251  */
252 void
_canceloff_nocancel()253 _canceloff_nocancel()
254 {
255 	ulwp_t *self = curthread;
256 
257 	ASSERT(!(self->ul_cancelable && self->ul_cancel_disabled));
258 	if (!self->ul_cancel_disabled) {
259 		self->ul_cancelable--;
260 		ASSERT(self->ul_cancelable >= 0);
261 	}
262 }
263 
264 /*
265  * __pthread_cleanup_push: called by macro in pthread.h which defines
266  * POSIX.1c pthread_cleanup_push(). Macro in pthread.h allocates the
267  * cleanup struct and calls this routine to push the handler off the
268  * curthread's struct.
269  */
270 void
__pthread_cleanup_push(void (* routine)(void *),void * args,caddr_t fp,_cleanup_t * clnup_info)271 __pthread_cleanup_push(void (*routine)(void *),
272     void *args, caddr_t fp, _cleanup_t *clnup_info)
273 {
274 	ulwp_t *self = curthread;
275 	__cleanup_t *infop = (__cleanup_t *)clnup_info;
276 
277 	infop->func = routine;
278 	infop->arg = args;
279 	infop->fp = fp;
280 	infop->next = self->ul_clnup_hdr;
281 	self->ul_clnup_hdr = infop;
282 }
283 
284 /*
285  * __pthread_cleanup_pop: called by macro in pthread.h which defines
286  * POSIX.1c pthread_cleanup_pop(). It calls this routine to pop the
287  * handler off the curthread's struct and execute it if necessary.
288  */
289 void
__pthread_cleanup_pop(int ex,_cleanup_t * clnup_info __unused)290 __pthread_cleanup_pop(int ex, _cleanup_t *clnup_info __unused)
291 {
292 	ulwp_t *self = curthread;
293 	__cleanup_t *infop = self->ul_clnup_hdr;
294 
295 	self->ul_clnup_hdr = infop->next;
296 	if (ex)
297 		(*infop->func)(infop->arg);
298 }
299 
300 /*
301  * Called when either self->ul_cancel_disabled or self->ul_cancel_pending
302  * is modified.  Setting SC_CANCEL_FLG informs the kernel that we have
303  * a pending cancellation and we do not have cancellation disabled.
304  * In this situation, we will not go to sleep on any system call but
305  * will instead return EINTR immediately on any attempt to sleep,
306  * with SC_EINTR_FLG set in sc_flgs.  Clearing SC_CANCEL_FLG rescinds
307  * this condition, but SC_EINTR_FLG never goes away until the thread
308  * terminates (indicated by clear_flags != 0).
309  */
310 void
set_cancel_pending_flag(ulwp_t * self,int clear_flags)311 set_cancel_pending_flag(ulwp_t *self, int clear_flags)
312 {
313 	volatile sc_shared_t *scp;
314 
315 	if (self->ul_vfork | self->ul_nocancel)
316 		return;
317 	enter_critical(self);
318 	if ((scp = self->ul_schedctl) != NULL ||
319 	    (scp = setup_schedctl()) != NULL) {
320 		if (clear_flags)
321 			scp->sc_flgs &= ~(SC_CANCEL_FLG | SC_EINTR_FLG);
322 		else if (self->ul_cancel_pending && !self->ul_cancel_disabled)
323 			scp->sc_flgs |= SC_CANCEL_FLG;
324 		else
325 			scp->sc_flgs &= ~SC_CANCEL_FLG;
326 	}
327 	exit_critical(self);
328 }
329 
330 /*
331  * Called from the PROLOGUE macro in scalls.c to inform subsequent
332  * code that a cancellation point has been called and that the
333  * current thread should cancel itself as soon as all of its locks
334  * have been dropped (see safe_mutex_unlock()).
335  */
336 void
set_cancel_eintr_flag(ulwp_t * self)337 set_cancel_eintr_flag(ulwp_t *self)
338 {
339 	volatile sc_shared_t *scp;
340 
341 	if (self->ul_vfork | self->ul_nocancel)
342 		return;
343 	enter_critical(self);
344 	if ((scp = self->ul_schedctl) != NULL ||
345 	    (scp = setup_schedctl()) != NULL)
346 		scp->sc_flgs |= SC_EINTR_FLG;
347 	exit_critical(self);
348 }
349 
350 /*
351  * Calling set_parking_flag(curthread, 1) informs the kernel that we are
352  * calling __lwp_park or ___lwp_cond_wait().  If we take a signal in
353  * the unprotected (from signals) interval before reaching the kernel,
354  * sigacthandler() will call set_parking_flag(curthread, 0) to inform
355  * the kernel to return immediately from these system calls, giving us
356  * a spurious wakeup but not a deadlock.
357  */
358 void
set_parking_flag(ulwp_t * self,int park)359 set_parking_flag(ulwp_t *self, int park)
360 {
361 	volatile sc_shared_t *scp;
362 
363 	enter_critical(self);
364 	if ((scp = self->ul_schedctl) != NULL ||
365 	    (scp = setup_schedctl()) != NULL) {
366 		if (park) {
367 			scp->sc_flgs |= SC_PARK_FLG;
368 			/*
369 			 * We are parking; allow the __lwp_park() call to
370 			 * block even if we have a pending cancellation.
371 			 */
372 			scp->sc_flgs &= ~SC_CANCEL_FLG;
373 		} else {
374 			scp->sc_flgs &= ~(SC_PARK_FLG | SC_CANCEL_FLG);
375 			/*
376 			 * We are no longer parking; restore the
377 			 * pending cancellation flag if necessary.
378 			 */
379 			if (self->ul_cancel_pending &&
380 			    !self->ul_cancel_disabled)
381 				scp->sc_flgs |= SC_CANCEL_FLG;
382 		}
383 	} else if (park == 0) {	/* schedctl failed, do it the long way */
384 		(void) __lwp_unpark(self->ul_lwpid);
385 	}
386 	exit_critical(self);
387 }
388 
389 /*
390  * Test if the current thread is due to exit because of cancellation.
391  */
392 int
cancel_active(void)393 cancel_active(void)
394 {
395 	ulwp_t *self = curthread;
396 	volatile sc_shared_t *scp;
397 	int exit_soon;
398 
399 	/*
400 	 * If there is a pending cancellation and cancellation
401 	 * is not disabled (SC_CANCEL_FLG) and we received
402 	 * EINTR from a recent system call (SC_EINTR_FLG),
403 	 * then we will soon be exiting.
404 	 */
405 	enter_critical(self);
406 	exit_soon =
407 	    (((scp = self->ul_schedctl) != NULL ||
408 	    (scp = setup_schedctl()) != NULL) &&
409 	    (scp->sc_flgs & (SC_CANCEL_FLG | SC_EINTR_FLG)) ==
410 	    (SC_CANCEL_FLG | SC_EINTR_FLG));
411 	exit_critical(self);
412 
413 	return (exit_soon);
414 }
415