xref: /onnv-gate/usr/src/lib/libc/port/threads/cancel.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate #include "lint.h"
30*0Sstevel@tonic-gate #include "thr_uberdata.h"
31*0Sstevel@tonic-gate 
32*0Sstevel@tonic-gate /*
33*0Sstevel@tonic-gate  * pthread_cancel: tries to cancel the targeted thread.
34*0Sstevel@tonic-gate  * If the target thread has already exited no action is taken.
35*0Sstevel@tonic-gate  * Else send SIGCANCEL to request the other thread to cancel itself.
36*0Sstevel@tonic-gate  */
37*0Sstevel@tonic-gate #pragma weak pthread_cancel = _pthread_cancel
38*0Sstevel@tonic-gate int
39*0Sstevel@tonic-gate _pthread_cancel(thread_t tid)
40*0Sstevel@tonic-gate {
41*0Sstevel@tonic-gate 	ulwp_t *self = curthread;
42*0Sstevel@tonic-gate 	uberdata_t *udp = self->ul_uberdata;
43*0Sstevel@tonic-gate 	ulwp_t *ulwp;
44*0Sstevel@tonic-gate 	int error = 0;
45*0Sstevel@tonic-gate 
46*0Sstevel@tonic-gate 	if ((ulwp = find_lwp(tid)) == NULL)
47*0Sstevel@tonic-gate 		return (ESRCH);
48*0Sstevel@tonic-gate 
49*0Sstevel@tonic-gate 	if (ulwp->ul_cancel_pending) {
50*0Sstevel@tonic-gate 		/*
51*0Sstevel@tonic-gate 		 * Don't send SIGCANCEL more than once.
52*0Sstevel@tonic-gate 		 */
53*0Sstevel@tonic-gate 		ulwp_unlock(ulwp, udp);
54*0Sstevel@tonic-gate 	} else if (ulwp == self) {
55*0Sstevel@tonic-gate 		/*
56*0Sstevel@tonic-gate 		 * Unlock self before cancelling.
57*0Sstevel@tonic-gate 		 */
58*0Sstevel@tonic-gate 		ulwp_unlock(ulwp, udp);
59*0Sstevel@tonic-gate 		ulwp->ul_nocancel = 0;	/* cancellation is now possible */
60*0Sstevel@tonic-gate 		if (ulwp->ul_sigdefer)
61*0Sstevel@tonic-gate 			ulwp->ul_cancel_pending = 1;
62*0Sstevel@tonic-gate 		else
63*0Sstevel@tonic-gate 			do_sigcancel();
64*0Sstevel@tonic-gate 	} else if (ulwp->ul_cancel_disabled) {
65*0Sstevel@tonic-gate 		/*
66*0Sstevel@tonic-gate 		 * Don't send SIGCANCEL if cancellation is disabled;
67*0Sstevel@tonic-gate 		 * just set the thread's ulwp->ul_cancel_pending flag.
68*0Sstevel@tonic-gate 		 * This avoids a potential EINTR for the target thread.
69*0Sstevel@tonic-gate 		 */
70*0Sstevel@tonic-gate 		ulwp->ul_cancel_pending = 1;
71*0Sstevel@tonic-gate 		ulwp_unlock(ulwp, udp);
72*0Sstevel@tonic-gate 	} else {
73*0Sstevel@tonic-gate 		/*
74*0Sstevel@tonic-gate 		 * Request the other thread to cancel itself.
75*0Sstevel@tonic-gate 		 */
76*0Sstevel@tonic-gate 		error = __lwp_kill(tid, SIGCANCEL);
77*0Sstevel@tonic-gate 		ulwp_unlock(ulwp, udp);
78*0Sstevel@tonic-gate 	}
79*0Sstevel@tonic-gate 
80*0Sstevel@tonic-gate 	return (error);
81*0Sstevel@tonic-gate }
82*0Sstevel@tonic-gate 
83*0Sstevel@tonic-gate /*
84*0Sstevel@tonic-gate  * pthread_setcancelstate: sets the state ENABLED or DISABLED
85*0Sstevel@tonic-gate  * If the state is being set as ENABLED, then it becomes
86*0Sstevel@tonic-gate  * a cancellation point only if the type of cancellation is
87*0Sstevel@tonic-gate  * ASYNCHRONOUS and a cancel request is pending.
88*0Sstevel@tonic-gate  * Disabling cancellation is not a cancellation point.
89*0Sstevel@tonic-gate  */
90*0Sstevel@tonic-gate #pragma weak pthread_setcancelstate = _pthread_setcancelstate
91*0Sstevel@tonic-gate int
92*0Sstevel@tonic-gate _pthread_setcancelstate(int state, int *oldstate)
93*0Sstevel@tonic-gate {
94*0Sstevel@tonic-gate 	ulwp_t *self = curthread;
95*0Sstevel@tonic-gate 	uberdata_t *udp = self->ul_uberdata;
96*0Sstevel@tonic-gate 	int was_disabled;
97*0Sstevel@tonic-gate 
98*0Sstevel@tonic-gate 	/*
99*0Sstevel@tonic-gate 	 * Grab ulwp_lock(self) to protect the setting of ul_cancel_disabled
100*0Sstevel@tonic-gate 	 * since it is tested under this lock by pthread_cancel(), above.
101*0Sstevel@tonic-gate 	 * This has the side-effect of calling enter_critical() and this
102*0Sstevel@tonic-gate 	 * defers SIGCANCEL until ulwp_unlock(self) when exit_critical()
103*0Sstevel@tonic-gate 	 * is called.  (self->ul_cancel_pending is set in the SIGCANCEL
104*0Sstevel@tonic-gate 	 * handler and we must be async-signal safe here.)
105*0Sstevel@tonic-gate 	 */
106*0Sstevel@tonic-gate 	ulwp_lock(self, udp);
107*0Sstevel@tonic-gate 
108*0Sstevel@tonic-gate 	was_disabled = self->ul_cancel_disabled;
109*0Sstevel@tonic-gate 	switch (state) {
110*0Sstevel@tonic-gate 	case PTHREAD_CANCEL_ENABLE:
111*0Sstevel@tonic-gate 		self->ul_cancel_disabled = 0;
112*0Sstevel@tonic-gate 		break;
113*0Sstevel@tonic-gate 	case PTHREAD_CANCEL_DISABLE:
114*0Sstevel@tonic-gate 		self->ul_cancel_disabled = 1;
115*0Sstevel@tonic-gate 		break;
116*0Sstevel@tonic-gate 	default:
117*0Sstevel@tonic-gate 		ulwp_unlock(self, udp);
118*0Sstevel@tonic-gate 		return (EINVAL);
119*0Sstevel@tonic-gate 	}
120*0Sstevel@tonic-gate 
121*0Sstevel@tonic-gate 	/*
122*0Sstevel@tonic-gate 	 * If this thread has been requested to be canceled and
123*0Sstevel@tonic-gate 	 * is in async mode and is or was enabled, then exit.
124*0Sstevel@tonic-gate 	 */
125*0Sstevel@tonic-gate 	if ((!self->ul_cancel_disabled || !was_disabled) &&
126*0Sstevel@tonic-gate 	    self->ul_cancel_async && self->ul_cancel_pending) {
127*0Sstevel@tonic-gate 		ulwp_unlock(self, udp);
128*0Sstevel@tonic-gate 		_pthread_exit(PTHREAD_CANCELED);
129*0Sstevel@tonic-gate 	}
130*0Sstevel@tonic-gate 
131*0Sstevel@tonic-gate 	ulwp_unlock(self, udp);
132*0Sstevel@tonic-gate 
133*0Sstevel@tonic-gate 	if (oldstate != NULL) {
134*0Sstevel@tonic-gate 		if (was_disabled)
135*0Sstevel@tonic-gate 			*oldstate = PTHREAD_CANCEL_DISABLE;
136*0Sstevel@tonic-gate 		else
137*0Sstevel@tonic-gate 			*oldstate = PTHREAD_CANCEL_ENABLE;
138*0Sstevel@tonic-gate 	}
139*0Sstevel@tonic-gate 	return (0);
140*0Sstevel@tonic-gate }
141*0Sstevel@tonic-gate 
142*0Sstevel@tonic-gate /*
143*0Sstevel@tonic-gate  * pthread_setcanceltype: sets the type DEFERRED or ASYNCHRONOUS
144*0Sstevel@tonic-gate  * If the type is being set as ASYNC, then it becomes
145*0Sstevel@tonic-gate  * a cancellation point if there is a cancellation pending.
146*0Sstevel@tonic-gate  */
147*0Sstevel@tonic-gate #pragma weak pthread_setcanceltype = _pthread_setcanceltype
148*0Sstevel@tonic-gate int
149*0Sstevel@tonic-gate _pthread_setcanceltype(int type, int *oldtype)
150*0Sstevel@tonic-gate {
151*0Sstevel@tonic-gate 	ulwp_t *self = curthread;
152*0Sstevel@tonic-gate 	int was_async;
153*0Sstevel@tonic-gate 
154*0Sstevel@tonic-gate 	/*
155*0Sstevel@tonic-gate 	 * Call enter_critical() to defer SIGCANCEL until exit_critical().
156*0Sstevel@tonic-gate 	 * We do this because curthread->ul_cancel_pending is set in the
157*0Sstevel@tonic-gate 	 * SIGCANCEL handler and we must be async-signal safe here.
158*0Sstevel@tonic-gate 	 */
159*0Sstevel@tonic-gate 	enter_critical(self);
160*0Sstevel@tonic-gate 
161*0Sstevel@tonic-gate 	was_async = self->ul_cancel_async;
162*0Sstevel@tonic-gate 	switch (type) {
163*0Sstevel@tonic-gate 	case PTHREAD_CANCEL_ASYNCHRONOUS:
164*0Sstevel@tonic-gate 		self->ul_cancel_async = 1;
165*0Sstevel@tonic-gate 		break;
166*0Sstevel@tonic-gate 	case PTHREAD_CANCEL_DEFERRED:
167*0Sstevel@tonic-gate 		self->ul_cancel_async = 0;
168*0Sstevel@tonic-gate 		break;
169*0Sstevel@tonic-gate 	default:
170*0Sstevel@tonic-gate 		exit_critical(self);
171*0Sstevel@tonic-gate 		return (EINVAL);
172*0Sstevel@tonic-gate 	}
173*0Sstevel@tonic-gate 	self->ul_save_async = self->ul_cancel_async;
174*0Sstevel@tonic-gate 
175*0Sstevel@tonic-gate 	/*
176*0Sstevel@tonic-gate 	 * If this thread has been requested to be canceled and
177*0Sstevel@tonic-gate 	 * is in enabled mode and is or was in async mode, exit.
178*0Sstevel@tonic-gate 	 */
179*0Sstevel@tonic-gate 	if ((self->ul_cancel_async || was_async) &&
180*0Sstevel@tonic-gate 	    self->ul_cancel_pending && !self->ul_cancel_disabled) {
181*0Sstevel@tonic-gate 		exit_critical(self);
182*0Sstevel@tonic-gate 		_pthread_exit(PTHREAD_CANCELED);
183*0Sstevel@tonic-gate 	}
184*0Sstevel@tonic-gate 
185*0Sstevel@tonic-gate 	exit_critical(self);
186*0Sstevel@tonic-gate 
187*0Sstevel@tonic-gate 	if (oldtype != NULL) {
188*0Sstevel@tonic-gate 		if (was_async)
189*0Sstevel@tonic-gate 			*oldtype = PTHREAD_CANCEL_ASYNCHRONOUS;
190*0Sstevel@tonic-gate 		else
191*0Sstevel@tonic-gate 			*oldtype = PTHREAD_CANCEL_DEFERRED;
192*0Sstevel@tonic-gate 	}
193*0Sstevel@tonic-gate 	return (0);
194*0Sstevel@tonic-gate }
195*0Sstevel@tonic-gate 
196*0Sstevel@tonic-gate /*
197*0Sstevel@tonic-gate  * pthread_testcancel: tests for any cancellation pending
198*0Sstevel@tonic-gate  * if the cancellation is enabled and is pending, act on
199*0Sstevel@tonic-gate  * it by calling thr_exit. thr_exit takes care of calling
200*0Sstevel@tonic-gate  * cleanup handlers.
201*0Sstevel@tonic-gate  */
202*0Sstevel@tonic-gate #pragma weak _private_testcancel = _pthread_testcancel
203*0Sstevel@tonic-gate #pragma weak pthread_testcancel = _pthread_testcancel
204*0Sstevel@tonic-gate void
205*0Sstevel@tonic-gate _pthread_testcancel(void)
206*0Sstevel@tonic-gate {
207*0Sstevel@tonic-gate 	ulwp_t *self = curthread;
208*0Sstevel@tonic-gate 
209*0Sstevel@tonic-gate 	if (self->ul_cancel_pending && !self->ul_cancel_disabled)
210*0Sstevel@tonic-gate 		_pthread_exit(PTHREAD_CANCELED);
211*0Sstevel@tonic-gate }
212*0Sstevel@tonic-gate 
213*0Sstevel@tonic-gate /*
214*0Sstevel@tonic-gate  * For deferred mode, this routine makes a thread cancelable.
215*0Sstevel@tonic-gate  * It is called from the functions which want to be cancellation
216*0Sstevel@tonic-gate  * points and are about to block, such as cond_wait().
217*0Sstevel@tonic-gate  */
218*0Sstevel@tonic-gate void
219*0Sstevel@tonic-gate _cancelon()
220*0Sstevel@tonic-gate {
221*0Sstevel@tonic-gate 	ulwp_t *self = curthread;
222*0Sstevel@tonic-gate 
223*0Sstevel@tonic-gate 	ASSERT(!(self->ul_cancelable && self->ul_cancel_disabled));
224*0Sstevel@tonic-gate 	if (!self->ul_cancel_disabled) {
225*0Sstevel@tonic-gate 		ASSERT(self->ul_cancelable >= 0);
226*0Sstevel@tonic-gate 		self->ul_cancelable++;
227*0Sstevel@tonic-gate 		if (self->ul_cancel_pending)
228*0Sstevel@tonic-gate 			_pthread_exit(PTHREAD_CANCELED);
229*0Sstevel@tonic-gate 	}
230*0Sstevel@tonic-gate }
231*0Sstevel@tonic-gate 
232*0Sstevel@tonic-gate /*
233*0Sstevel@tonic-gate  * This routine turns cancelability off and possible calls pthread_exit().
234*0Sstevel@tonic-gate  * It is called from functions which are cancellation points, like cond_wait().
235*0Sstevel@tonic-gate  */
236*0Sstevel@tonic-gate void
237*0Sstevel@tonic-gate _canceloff()
238*0Sstevel@tonic-gate {
239*0Sstevel@tonic-gate 	ulwp_t *self = curthread;
240*0Sstevel@tonic-gate 
241*0Sstevel@tonic-gate 	ASSERT(!(self->ul_cancelable && self->ul_cancel_disabled));
242*0Sstevel@tonic-gate 	if (!self->ul_cancel_disabled) {
243*0Sstevel@tonic-gate 		if (self->ul_cancel_pending)
244*0Sstevel@tonic-gate 			_pthread_exit(PTHREAD_CANCELED);
245*0Sstevel@tonic-gate 		self->ul_cancelable--;
246*0Sstevel@tonic-gate 		ASSERT(self->ul_cancelable >= 0);
247*0Sstevel@tonic-gate 	}
248*0Sstevel@tonic-gate }
249*0Sstevel@tonic-gate 
250*0Sstevel@tonic-gate /*
251*0Sstevel@tonic-gate  * Same as _canceloff() but don't actually cancel the thread.
252*0Sstevel@tonic-gate  * This is used by cond_wait() and sema_wait() when they don't get EINTR.
253*0Sstevel@tonic-gate  */
254*0Sstevel@tonic-gate void
255*0Sstevel@tonic-gate _canceloff_nocancel()
256*0Sstevel@tonic-gate {
257*0Sstevel@tonic-gate 	ulwp_t *self = curthread;
258*0Sstevel@tonic-gate 
259*0Sstevel@tonic-gate 	ASSERT(!(self->ul_cancelable && self->ul_cancel_disabled));
260*0Sstevel@tonic-gate 	if (!self->ul_cancel_disabled) {
261*0Sstevel@tonic-gate 		self->ul_cancelable--;
262*0Sstevel@tonic-gate 		ASSERT(self->ul_cancelable >= 0);
263*0Sstevel@tonic-gate 	}
264*0Sstevel@tonic-gate }
265*0Sstevel@tonic-gate 
266*0Sstevel@tonic-gate /*
267*0Sstevel@tonic-gate  * __pthread_cleanup_push: called by macro in pthread.h which defines
268*0Sstevel@tonic-gate  * POSIX.1c pthread_cleanup_push(). Macro in pthread.h allocates the
269*0Sstevel@tonic-gate  * cleanup struct and calls this routine to push the handler off the
270*0Sstevel@tonic-gate  * curthread's struct.
271*0Sstevel@tonic-gate  */
272*0Sstevel@tonic-gate void
273*0Sstevel@tonic-gate __pthread_cleanup_push(void (*routine)(void *),
274*0Sstevel@tonic-gate 	void *args, caddr_t fp, _cleanup_t *clnup_info)
275*0Sstevel@tonic-gate {
276*0Sstevel@tonic-gate 	ulwp_t *self = curthread;
277*0Sstevel@tonic-gate 	__cleanup_t *infop = (__cleanup_t *)clnup_info;
278*0Sstevel@tonic-gate 
279*0Sstevel@tonic-gate 	infop->func = routine;
280*0Sstevel@tonic-gate 	infop->arg = args;
281*0Sstevel@tonic-gate 	infop->fp = fp;
282*0Sstevel@tonic-gate 	infop->next = self->ul_clnup_hdr;
283*0Sstevel@tonic-gate 	self->ul_clnup_hdr = infop;
284*0Sstevel@tonic-gate }
285*0Sstevel@tonic-gate 
286*0Sstevel@tonic-gate /*
287*0Sstevel@tonic-gate  * __pthread_cleanup_pop: called by macro in pthread.h which defines
288*0Sstevel@tonic-gate  * POSIX.1c pthread_cleanup_pop(). It calls this routine to pop the
289*0Sstevel@tonic-gate  * handler off the curthread's struct and execute it if necessary.
290*0Sstevel@tonic-gate  */
291*0Sstevel@tonic-gate /* ARGSUSED1 */
292*0Sstevel@tonic-gate void
293*0Sstevel@tonic-gate __pthread_cleanup_pop(int ex, _cleanup_t *clnup_info)
294*0Sstevel@tonic-gate {
295*0Sstevel@tonic-gate 	ulwp_t *self = curthread;
296*0Sstevel@tonic-gate 	__cleanup_t *infop = self->ul_clnup_hdr;
297*0Sstevel@tonic-gate 
298*0Sstevel@tonic-gate 	self->ul_clnup_hdr = infop->next;
299*0Sstevel@tonic-gate 	if (ex)
300*0Sstevel@tonic-gate 		(*infop->func)(infop->arg);
301*0Sstevel@tonic-gate }
302