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