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 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 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 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 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 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 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 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 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 /* ARGSUSED1 */ 290 void 291 __pthread_cleanup_pop(int ex, _cleanup_t *clnup_info) 292 { 293 ulwp_t *self = curthread; 294 __cleanup_t *infop = self->ul_clnup_hdr; 295 296 self->ul_clnup_hdr = infop->next; 297 if (ex) 298 (*infop->func)(infop->arg); 299 } 300 301 /* 302 * Called when either self->ul_cancel_disabled or self->ul_cancel_pending 303 * is modified. Setting SC_CANCEL_FLG informs the kernel that we have 304 * a pending cancellation and we do not have cancellation disabled. 305 * In this situation, we will not go to sleep on any system call but 306 * will instead return EINTR immediately on any attempt to sleep, 307 * with SC_EINTR_FLG set in sc_flgs. Clearing SC_CANCEL_FLG rescinds 308 * this condition, but SC_EINTR_FLG never goes away until the thread 309 * terminates (indicated by clear_flags != 0). 310 */ 311 void 312 set_cancel_pending_flag(ulwp_t *self, int clear_flags) 313 { 314 volatile sc_shared_t *scp; 315 316 if (self->ul_vfork | self->ul_nocancel) 317 return; 318 enter_critical(self); 319 if ((scp = self->ul_schedctl) != NULL || 320 (scp = setup_schedctl()) != NULL) { 321 if (clear_flags) 322 scp->sc_flgs &= ~(SC_CANCEL_FLG | SC_EINTR_FLG); 323 else if (self->ul_cancel_pending && !self->ul_cancel_disabled) 324 scp->sc_flgs |= SC_CANCEL_FLG; 325 else 326 scp->sc_flgs &= ~SC_CANCEL_FLG; 327 } 328 exit_critical(self); 329 } 330 331 /* 332 * Called from the PROLOGUE macro in scalls.c to inform subsequent 333 * code that a cancellation point has been called and that the 334 * current thread should cancel itself as soon as all of its locks 335 * have been dropped (see safe_mutex_unlock()). 336 */ 337 void 338 set_cancel_eintr_flag(ulwp_t *self) 339 { 340 volatile sc_shared_t *scp; 341 342 if (self->ul_vfork | self->ul_nocancel) 343 return; 344 enter_critical(self); 345 if ((scp = self->ul_schedctl) != NULL || 346 (scp = setup_schedctl()) != NULL) 347 scp->sc_flgs |= SC_EINTR_FLG; 348 exit_critical(self); 349 } 350 351 /* 352 * Calling set_parking_flag(curthread, 1) informs the kernel that we are 353 * calling __lwp_park or ___lwp_cond_wait(). If we take a signal in 354 * the unprotected (from signals) interval before reaching the kernel, 355 * sigacthandler() will call set_parking_flag(curthread, 0) to inform 356 * the kernel to return immediately from these system calls, giving us 357 * a spurious wakeup but not a deadlock. 358 */ 359 void 360 set_parking_flag(ulwp_t *self, int park) 361 { 362 volatile sc_shared_t *scp; 363 364 enter_critical(self); 365 if ((scp = self->ul_schedctl) != NULL || 366 (scp = setup_schedctl()) != NULL) { 367 if (park) { 368 scp->sc_flgs |= SC_PARK_FLG; 369 /* 370 * We are parking; allow the __lwp_park() call to 371 * block even if we have a pending cancellation. 372 */ 373 scp->sc_flgs &= ~SC_CANCEL_FLG; 374 } else { 375 scp->sc_flgs &= ~(SC_PARK_FLG | SC_CANCEL_FLG); 376 /* 377 * We are no longer parking; restore the 378 * pending cancellation flag if necessary. 379 */ 380 if (self->ul_cancel_pending && 381 !self->ul_cancel_disabled) 382 scp->sc_flgs |= SC_CANCEL_FLG; 383 } 384 } else if (park == 0) { /* schedctl failed, do it the long way */ 385 (void) __lwp_unpark(self->ul_lwpid); 386 } 387 exit_critical(self); 388 } 389 390 /* 391 * Test if the current thread is due to exit because of cancellation. 392 */ 393 int 394 cancel_active(void) 395 { 396 ulwp_t *self = curthread; 397 volatile sc_shared_t *scp; 398 int exit_soon; 399 400 /* 401 * If there is a pending cancellation and cancellation 402 * is not disabled (SC_CANCEL_FLG) and we received 403 * EINTR from a recent system call (SC_EINTR_FLG), 404 * then we will soon be exiting. 405 */ 406 enter_critical(self); 407 exit_soon = 408 (((scp = self->ul_schedctl) != NULL || 409 (scp = setup_schedctl()) != NULL) && 410 (scp->sc_flgs & (SC_CANCEL_FLG | SC_EINTR_FLG)) == 411 (SC_CANCEL_FLG | SC_EINTR_FLG)); 412 exit_critical(self); 413 414 return (exit_soon); 415 } 416