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