1bb535300SJeff Roberson /* 2df2cf821SDavid Xu * Copyright (c) 2005, David Xu <davidxu@freebsd.org> 3bb535300SJeff Roberson * All rights reserved. 4bb535300SJeff Roberson * 5bb535300SJeff Roberson * Redistribution and use in source and binary forms, with or without 6bb535300SJeff Roberson * modification, are permitted provided that the following conditions 7bb535300SJeff Roberson * are met: 8bb535300SJeff Roberson * 1. Redistributions of source code must retain the above copyright 9df2cf821SDavid Xu * notice unmodified, this list of conditions, and the following 10df2cf821SDavid Xu * disclaimer. 11bb535300SJeff Roberson * 2. Redistributions in binary form must reproduce the above copyright 12bb535300SJeff Roberson * notice, this list of conditions and the following disclaimer in the 13bb535300SJeff Roberson * documentation and/or other materials provided with the distribution. 14bb535300SJeff Roberson * 15df2cf821SDavid Xu * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16df2cf821SDavid Xu * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17df2cf821SDavid Xu * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18df2cf821SDavid Xu * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19df2cf821SDavid Xu * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20df2cf821SDavid Xu * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21df2cf821SDavid Xu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22df2cf821SDavid Xu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23df2cf821SDavid Xu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24df2cf821SDavid Xu * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25bb535300SJeff Roberson * 26bb535300SJeff Roberson * $FreeBSD$ 27bb535300SJeff Roberson */ 28bb535300SJeff Roberson 2937a6356bSDavid Xu #include "namespace.h" 30bb535300SJeff Roberson #include <sys/param.h> 31bb535300SJeff Roberson #include <sys/types.h> 32bb535300SJeff Roberson #include <sys/signalvar.h> 33bb535300SJeff Roberson #include <signal.h> 34a091d823SDavid Xu #include <errno.h> 3510280ca6SKonstantin Belousov #include <stdlib.h> 36a091d823SDavid Xu #include <string.h> 37bb535300SJeff Roberson #include <pthread.h> 3837a6356bSDavid Xu #include "un-namespace.h" 3902c3c858SDavid Xu #include "libc_private.h" 4089552201SMike Makonnen 418495e8b1SKonstantin Belousov #include "libc_private.h" 42bb535300SJeff Roberson #include "thr_private.h" 43bb535300SJeff Roberson 44bb535300SJeff Roberson /* #define DEBUG_SIGNAL */ 45bb535300SJeff Roberson #ifdef DEBUG_SIGNAL 46bb535300SJeff Roberson #define DBG_MSG stdout_debug 47bb535300SJeff Roberson #else 48bb535300SJeff Roberson #define DBG_MSG(x...) 49bb535300SJeff Roberson #endif 50bb535300SJeff Roberson 5102c3c858SDavid Xu struct usigaction { 5202c3c858SDavid Xu struct sigaction sigact; 5302c3c858SDavid Xu struct urwlock lock; 5402c3c858SDavid Xu }; 5502c3c858SDavid Xu 5602c3c858SDavid Xu static struct usigaction _thr_sigact[_SIG_MAXSIG]; 5702c3c858SDavid Xu 588495e8b1SKonstantin Belousov static inline struct usigaction * 598495e8b1SKonstantin Belousov __libc_sigaction_slot(int signo) 608495e8b1SKonstantin Belousov { 618495e8b1SKonstantin Belousov 628495e8b1SKonstantin Belousov return (&_thr_sigact[signo - 1]); 638495e8b1SKonstantin Belousov } 648495e8b1SKonstantin Belousov 6502c3c858SDavid Xu static void thr_sighandler(int, siginfo_t *, void *); 6602c3c858SDavid Xu static void handle_signal(struct sigaction *, int, siginfo_t *, ucontext_t *); 6702c3c858SDavid Xu static void check_deferred_signal(struct pthread *); 6802c3c858SDavid Xu static void check_suspend(struct pthread *); 6902c3c858SDavid Xu static void check_cancel(struct pthread *curthread, ucontext_t *ucp); 7002c3c858SDavid Xu 71922d56f9SDavid Xu int _sigtimedwait(const sigset_t *set, siginfo_t *info, 72922d56f9SDavid Xu const struct timespec * timeout); 73922d56f9SDavid Xu int _sigwaitinfo(const sigset_t *set, siginfo_t *info); 74922d56f9SDavid Xu int _sigwait(const sigset_t *set, int *sig); 755cf22195SDavid Xu int _setcontext(const ucontext_t *); 765cf22195SDavid Xu int _swapcontext(ucontext_t *, const ucontext_t *); 7737a6356bSDavid Xu 7802c3c858SDavid Xu static const sigset_t _thr_deferset={{ 7902c3c858SDavid Xu 0xffffffff & ~(_SIG_BIT(SIGBUS)|_SIG_BIT(SIGILL)|_SIG_BIT(SIGFPE)| 8002c3c858SDavid Xu _SIG_BIT(SIGSEGV)|_SIG_BIT(SIGTRAP)|_SIG_BIT(SIGSYS)), 8102c3c858SDavid Xu 0xffffffff, 8202c3c858SDavid Xu 0xffffffff, 8302c3c858SDavid Xu 0xffffffff}}; 8402c3c858SDavid Xu 8502c3c858SDavid Xu static const sigset_t _thr_maskset={{ 8602c3c858SDavid Xu 0xffffffff, 8702c3c858SDavid Xu 0xffffffff, 8802c3c858SDavid Xu 0xffffffff, 8902c3c858SDavid Xu 0xffffffff}}; 9002c3c858SDavid Xu 9102c3c858SDavid Xu void 9202c3c858SDavid Xu _thr_signal_block(struct pthread *curthread) 9302c3c858SDavid Xu { 9402c3c858SDavid Xu 9502c3c858SDavid Xu if (curthread->sigblock > 0) { 9602c3c858SDavid Xu curthread->sigblock++; 9702c3c858SDavid Xu return; 9802c3c858SDavid Xu } 9902c3c858SDavid Xu __sys_sigprocmask(SIG_BLOCK, &_thr_maskset, &curthread->sigmask); 10002c3c858SDavid Xu curthread->sigblock++; 10102c3c858SDavid Xu } 10202c3c858SDavid Xu 10302c3c858SDavid Xu void 10402c3c858SDavid Xu _thr_signal_unblock(struct pthread *curthread) 10502c3c858SDavid Xu { 10602c3c858SDavid Xu if (--curthread->sigblock == 0) 10702c3c858SDavid Xu __sys_sigprocmask(SIG_SETMASK, &curthread->sigmask, NULL); 10802c3c858SDavid Xu } 10902c3c858SDavid Xu 11002c3c858SDavid Xu int 11102c3c858SDavid Xu _thr_send_sig(struct pthread *thread, int sig) 11202c3c858SDavid Xu { 11302c3c858SDavid Xu return thr_kill(thread->tid, sig); 11402c3c858SDavid Xu } 11502c3c858SDavid Xu 11602c3c858SDavid Xu static inline void 1175cf22195SDavid Xu remove_thr_signals(sigset_t *set) 1185cf22195SDavid Xu { 1195cf22195SDavid Xu if (SIGISMEMBER(*set, SIGCANCEL)) 1205cf22195SDavid Xu SIGDELSET(*set, SIGCANCEL); 1215cf22195SDavid Xu } 1225cf22195SDavid Xu 1235cf22195SDavid Xu static const sigset_t * 1245cf22195SDavid Xu thr_remove_thr_signals(const sigset_t *set, sigset_t *newset) 1255cf22195SDavid Xu { 1265cf22195SDavid Xu *newset = *set; 12702c3c858SDavid Xu remove_thr_signals(newset); 12802c3c858SDavid Xu return (newset); 1295cf22195SDavid Xu } 1306fdfcacbSDavid Xu 131a091d823SDavid Xu static void 13237a6356bSDavid Xu sigcancel_handler(int sig __unused, 13302c3c858SDavid Xu siginfo_t *info __unused, ucontext_t *ucp) 134a091d823SDavid Xu { 135a091d823SDavid Xu struct pthread *curthread = _get_curthread(); 13602c3c858SDavid Xu int err; 137a091d823SDavid Xu 13802c3c858SDavid Xu if (THR_IN_CRITICAL(curthread)) 13902c3c858SDavid Xu return; 14002c3c858SDavid Xu err = errno; 14102c3c858SDavid Xu check_suspend(curthread); 14202c3c858SDavid Xu check_cancel(curthread, ucp); 14302c3c858SDavid Xu errno = err; 14402c3c858SDavid Xu } 14502c3c858SDavid Xu 1468495e8b1SKonstantin Belousov typedef void (*ohandler)(int sig, int code, struct sigcontext *scp, 1478495e8b1SKonstantin Belousov char *addr, __sighandler_t *catcher); 14802c3c858SDavid Xu 14902c3c858SDavid Xu /* 15002c3c858SDavid Xu * The signal handler wrapper is entered with all signal masked. 15102c3c858SDavid Xu */ 15202c3c858SDavid Xu static void 15302c3c858SDavid Xu thr_sighandler(int sig, siginfo_t *info, void *_ucp) 15402c3c858SDavid Xu { 1558495e8b1SKonstantin Belousov struct pthread *curthread; 1568495e8b1SKonstantin Belousov ucontext_t *ucp; 15702c3c858SDavid Xu struct sigaction act; 1588495e8b1SKonstantin Belousov struct usigaction *usa; 15902c3c858SDavid Xu int err; 16002c3c858SDavid Xu 16102c3c858SDavid Xu err = errno; 1628495e8b1SKonstantin Belousov curthread = _get_curthread(); 1638495e8b1SKonstantin Belousov ucp = _ucp; 1648495e8b1SKonstantin Belousov usa = __libc_sigaction_slot(sig); 1658495e8b1SKonstantin Belousov _thr_rwl_rdlock(&usa->lock); 1668495e8b1SKonstantin Belousov act = usa->sigact; 1678495e8b1SKonstantin Belousov _thr_rwl_unlock(&usa->lock); 16802c3c858SDavid Xu errno = err; 1690a9655a0SKonstantin Belousov curthread->deferred_run = 0; 17002c3c858SDavid Xu 17102c3c858SDavid Xu /* 17202c3c858SDavid Xu * if a thread is in critical region, for example it holds low level locks, 17302c3c858SDavid Xu * try to defer the signal processing, however if the signal is synchronous 17402c3c858SDavid Xu * signal, it means a bad thing has happened, this is a programming error, 17502c3c858SDavid Xu * resuming fault point can not help anything (normally causes deadloop), 17602c3c858SDavid Xu * so here we let user code handle it immediately. 17702c3c858SDavid Xu */ 17802c3c858SDavid Xu if (THR_IN_CRITICAL(curthread) && SIGISMEMBER(_thr_deferset, sig)) { 17902c3c858SDavid Xu memcpy(&curthread->deferred_sigact, &act, sizeof(struct sigaction)); 18002c3c858SDavid Xu memcpy(&curthread->deferred_siginfo, info, sizeof(siginfo_t)); 18102c3c858SDavid Xu curthread->deferred_sigmask = ucp->uc_sigmask; 18202c3c858SDavid Xu /* mask all signals, we will restore it later. */ 18302c3c858SDavid Xu ucp->uc_sigmask = _thr_deferset; 18402c3c858SDavid Xu return; 18502c3c858SDavid Xu } 18602c3c858SDavid Xu 18702c3c858SDavid Xu handle_signal(&act, sig, info, ucp); 18802c3c858SDavid Xu } 18902c3c858SDavid Xu 19002c3c858SDavid Xu static void 19102c3c858SDavid Xu handle_signal(struct sigaction *actp, int sig, siginfo_t *info, ucontext_t *ucp) 19202c3c858SDavid Xu { 19302c3c858SDavid Xu struct pthread *curthread = _get_curthread(); 19402c3c858SDavid Xu ucontext_t uc2; 19502c3c858SDavid Xu __siginfohandler_t *sigfunc; 19602c3c858SDavid Xu int cancel_point; 19702c3c858SDavid Xu int cancel_async; 19802c3c858SDavid Xu int cancel_enable; 19902c3c858SDavid Xu int in_sigsuspend; 20002c3c858SDavid Xu int err; 20102c3c858SDavid Xu 20202c3c858SDavid Xu /* add previous level mask */ 20302c3c858SDavid Xu SIGSETOR(actp->sa_mask, ucp->uc_sigmask); 20402c3c858SDavid Xu 20502c3c858SDavid Xu /* add this signal's mask */ 20602c3c858SDavid Xu if (!(actp->sa_flags & SA_NODEFER)) 20702c3c858SDavid Xu SIGADDSET(actp->sa_mask, sig); 20802c3c858SDavid Xu 20902c3c858SDavid Xu in_sigsuspend = curthread->in_sigsuspend; 21002c3c858SDavid Xu curthread->in_sigsuspend = 0; 21102c3c858SDavid Xu 21202c3c858SDavid Xu /* 2135b1dd970SKonstantin Belousov * If thread is in deferred cancellation mode, disable cancellation 21402c3c858SDavid Xu * in signal handler. 2155b1dd970SKonstantin Belousov * If user signal handler calls a cancellation point function, e.g, 21602c3c858SDavid Xu * it calls write() to write data to file, because write() is a 21702c3c858SDavid Xu * cancellation point, the thread is immediately cancelled if 21802c3c858SDavid Xu * cancellation is pending, to avoid this problem while thread is in 21902c3c858SDavid Xu * deferring mode, cancellation is temporarily disabled. 22002c3c858SDavid Xu */ 22102c3c858SDavid Xu cancel_point = curthread->cancel_point; 22202c3c858SDavid Xu cancel_async = curthread->cancel_async; 22302c3c858SDavid Xu cancel_enable = curthread->cancel_enable; 22402c3c858SDavid Xu curthread->cancel_point = 0; 22502c3c858SDavid Xu if (!cancel_async) 22602c3c858SDavid Xu curthread->cancel_enable = 0; 22702c3c858SDavid Xu 22802c3c858SDavid Xu /* restore correct mask before calling user handler */ 22902c3c858SDavid Xu __sys_sigprocmask(SIG_SETMASK, &actp->sa_mask, NULL); 23002c3c858SDavid Xu 23102c3c858SDavid Xu sigfunc = actp->sa_sigaction; 23202c3c858SDavid Xu 23302c3c858SDavid Xu /* 23402c3c858SDavid Xu * We have already reset cancellation point flags, so if user's code 23502c3c858SDavid Xu * longjmp()s out of its signal handler, wish its jmpbuf was set 23602c3c858SDavid Xu * outside of a cancellation point, in most cases, this would be 2375b1dd970SKonstantin Belousov * true. However, there is no way to save cancel_enable in jmpbuf, 23802c3c858SDavid Xu * so after setjmps() returns once more, the user code may need to 23902c3c858SDavid Xu * re-set cancel_enable flag by calling pthread_setcancelstate(). 24002c3c858SDavid Xu */ 2418495e8b1SKonstantin Belousov if ((actp->sa_flags & SA_SIGINFO) != 0) { 2428495e8b1SKonstantin Belousov sigfunc(sig, info, ucp); 2438495e8b1SKonstantin Belousov } else { 2448495e8b1SKonstantin Belousov ((ohandler)sigfunc)(sig, info->si_code, 2458495e8b1SKonstantin Belousov (struct sigcontext *)ucp, info->si_addr, 2468495e8b1SKonstantin Belousov (__sighandler_t *)sigfunc); 24702c3c858SDavid Xu } 24802c3c858SDavid Xu err = errno; 24902c3c858SDavid Xu 25002c3c858SDavid Xu curthread->in_sigsuspend = in_sigsuspend; 25102c3c858SDavid Xu curthread->cancel_point = cancel_point; 25202c3c858SDavid Xu curthread->cancel_enable = cancel_enable; 25302c3c858SDavid Xu 25402c3c858SDavid Xu memcpy(&uc2, ucp, sizeof(uc2)); 25502c3c858SDavid Xu SIGDELSET(uc2.uc_sigmask, SIGCANCEL); 25602c3c858SDavid Xu 25702c3c858SDavid Xu /* reschedule cancellation */ 25802c3c858SDavid Xu check_cancel(curthread, &uc2); 25902c3c858SDavid Xu errno = err; 26002c3c858SDavid Xu __sys_sigreturn(&uc2); 261bc414752SDavid Xu } 262bc414752SDavid Xu 263bc414752SDavid Xu void 264bc414752SDavid Xu _thr_ast(struct pthread *curthread) 265bc414752SDavid Xu { 266635f917aSDavid Xu 26702c3c858SDavid Xu if (!THR_IN_CRITICAL(curthread)) { 26802c3c858SDavid Xu check_deferred_signal(curthread); 26902c3c858SDavid Xu check_suspend(curthread); 27002c3c858SDavid Xu check_cancel(curthread, NULL); 27102c3c858SDavid Xu } 27202c3c858SDavid Xu } 27302c3c858SDavid Xu 27402c3c858SDavid Xu /* reschedule cancellation */ 27502c3c858SDavid Xu static void 27602c3c858SDavid Xu check_cancel(struct pthread *curthread, ucontext_t *ucp) 27702c3c858SDavid Xu { 27802c3c858SDavid Xu 27981f3e99cSDavid Xu if (__predict_true(!curthread->cancel_pending || 28081f3e99cSDavid Xu !curthread->cancel_enable || curthread->no_cancel)) 281635f917aSDavid Xu return; 282635f917aSDavid Xu 283635f917aSDavid Xu /* 284635f917aSDavid Xu * Otherwise, we are in defer mode, and we are at 285635f917aSDavid Xu * cancel point, tell kernel to not block the current 286635f917aSDavid Xu * thread on next cancelable system call. 287635f917aSDavid Xu * 28802c3c858SDavid Xu * There are three cases we should call thr_wake() to 28902c3c858SDavid Xu * turn on TDP_WAKEUP or send SIGCANCEL in kernel: 290635f917aSDavid Xu * 1) we are going to call a cancelable system call, 291635f917aSDavid Xu * non-zero cancel_point means we are already in 292635f917aSDavid Xu * cancelable state, next system call is cancelable. 293635f917aSDavid Xu * 2) because _thr_ast() may be called by 294635f917aSDavid Xu * THR_CRITICAL_LEAVE() which is used by rtld rwlock 295635f917aSDavid Xu * and any libthr internal locks, when rtld rwlock 296*c5e7289cSKonstantin Belousov * is used, it is mostly caused by an unresolved PLT. 297*c5e7289cSKonstantin Belousov * Those routines may clear the TDP_WAKEUP flag by 298635f917aSDavid Xu * invoking some system calls, in those cases, we 299635f917aSDavid Xu * also should reenable the flag. 30002c3c858SDavid Xu * 3) thread is in sigsuspend(), and the syscall insists 30102c3c858SDavid Xu * on getting a signal before it agrees to return. 302635f917aSDavid Xu */ 303635f917aSDavid Xu if (curthread->cancel_point) { 30402c3c858SDavid Xu if (curthread->in_sigsuspend && ucp) { 30502c3c858SDavid Xu SIGADDSET(ucp->uc_sigmask, SIGCANCEL); 30602c3c858SDavid Xu curthread->unblock_sigcancel = 1; 30702c3c858SDavid Xu _thr_send_sig(curthread, SIGCANCEL); 30802c3c858SDavid Xu } else 309635f917aSDavid Xu thr_wake(curthread->tid); 31081f3e99cSDavid Xu } else if (curthread->cancel_async) { 31181f3e99cSDavid Xu /* 31281f3e99cSDavid Xu * asynchronous cancellation mode, act upon 31381f3e99cSDavid Xu * immediately. 31481f3e99cSDavid Xu */ 31581f3e99cSDavid Xu _pthread_exit_mask(PTHREAD_CANCELED, 31681f3e99cSDavid Xu ucp? &ucp->uc_sigmask : NULL); 317635f917aSDavid Xu } 318635f917aSDavid Xu } 319635f917aSDavid Xu 32002c3c858SDavid Xu static void 32102c3c858SDavid Xu check_deferred_signal(struct pthread *curthread) 32202c3c858SDavid Xu { 32310280ca6SKonstantin Belousov ucontext_t *uc; 32402c3c858SDavid Xu struct sigaction act; 32502c3c858SDavid Xu siginfo_t info; 32691ddaeb7SKonstantin Belousov int uc_len; 32702c3c858SDavid Xu 3280a9655a0SKonstantin Belousov if (__predict_true(curthread->deferred_siginfo.si_signo == 0 || 3290a9655a0SKonstantin Belousov curthread->deferred_run)) 33002c3c858SDavid Xu return; 33110280ca6SKonstantin Belousov 3320a9655a0SKonstantin Belousov curthread->deferred_run = 1; 33355a1911eSKonstantin Belousov uc_len = __getcontextx_size(); 33455a1911eSKonstantin Belousov uc = alloca(uc_len); 33555a1911eSKonstantin Belousov getcontext(uc); 3360a9655a0SKonstantin Belousov if (curthread->deferred_siginfo.si_signo == 0) { 3370a9655a0SKonstantin Belousov curthread->deferred_run = 0; 33855a1911eSKonstantin Belousov return; 3390a9655a0SKonstantin Belousov } 34055a1911eSKonstantin Belousov __fillcontextx2((char *)uc); 34102c3c858SDavid Xu act = curthread->deferred_sigact; 34210280ca6SKonstantin Belousov uc->uc_sigmask = curthread->deferred_sigmask; 34302c3c858SDavid Xu memcpy(&info, &curthread->deferred_siginfo, sizeof(siginfo_t)); 34402c3c858SDavid Xu /* remove signal */ 34502c3c858SDavid Xu curthread->deferred_siginfo.si_signo = 0; 34610280ca6SKonstantin Belousov handle_signal(&act, info.si_signo, &info, uc); 34702c3c858SDavid Xu } 348a091d823SDavid Xu 34902c3c858SDavid Xu static void 35002c3c858SDavid Xu check_suspend(struct pthread *curthread) 351a091d823SDavid Xu { 3528d6a11a0SDavid Xu uint32_t cycle; 35302c3c858SDavid Xu 35402c3c858SDavid Xu if (__predict_true((curthread->flags & 35502c3c858SDavid Xu (THR_FLAGS_NEED_SUSPEND | THR_FLAGS_SUSPENDED)) 35602c3c858SDavid Xu != THR_FLAGS_NEED_SUSPEND)) 35702c3c858SDavid Xu return; 358a7b84c65SDavid Xu if (curthread == _single_thread) 359a7b84c65SDavid Xu return; 3602ea1f90aSDavid Xu if (curthread->force_exit) 3612ea1f90aSDavid Xu return; 3622ea1f90aSDavid Xu 363bc414752SDavid Xu /* 364bc414752SDavid Xu * Blocks SIGCANCEL which other threads must send. 365bc414752SDavid Xu */ 366a091d823SDavid Xu _thr_signal_block(curthread); 367bc414752SDavid Xu 368bc414752SDavid Xu /* 369bc414752SDavid Xu * Increase critical_count, here we don't use THR_LOCK/UNLOCK 370bc414752SDavid Xu * because we are leaf code, we don't want to recursively call 371bc414752SDavid Xu * ourself. 372bc414752SDavid Xu */ 373bc414752SDavid Xu curthread->critical_count++; 374bddd24cdSDavid Xu THR_UMUTEX_LOCK(curthread, &(curthread)->lock); 375bc414752SDavid Xu while ((curthread->flags & (THR_FLAGS_NEED_SUSPEND | 376bc414752SDavid Xu THR_FLAGS_SUSPENDED)) == THR_FLAGS_NEED_SUSPEND) { 377bc414752SDavid Xu curthread->cycle++; 378a091d823SDavid Xu cycle = curthread->cycle; 379bc414752SDavid Xu 380bc414752SDavid Xu /* Wake the thread suspending us. */ 3818d6a11a0SDavid Xu _thr_umtx_wake(&curthread->cycle, INT_MAX, 0); 382bc414752SDavid Xu 383bc414752SDavid Xu /* 384bc414752SDavid Xu * if we are from pthread_exit, we don't want to 385bc414752SDavid Xu * suspend, just go and die. 386bc414752SDavid Xu */ 387bc414752SDavid Xu if (curthread->state == PS_DEAD) 388bc414752SDavid Xu break; 389bc414752SDavid Xu curthread->flags |= THR_FLAGS_SUSPENDED; 390bddd24cdSDavid Xu THR_UMUTEX_UNLOCK(curthread, &(curthread)->lock); 3918d6a11a0SDavid Xu _thr_umtx_wait_uint(&curthread->cycle, cycle, NULL, 0); 392bddd24cdSDavid Xu THR_UMUTEX_LOCK(curthread, &(curthread)->lock); 393a091d823SDavid Xu curthread->flags &= ~THR_FLAGS_SUSPENDED; 394a091d823SDavid Xu } 395bddd24cdSDavid Xu THR_UMUTEX_UNLOCK(curthread, &(curthread)->lock); 396bc414752SDavid Xu curthread->critical_count--; 397bc414752SDavid Xu 398a091d823SDavid Xu _thr_signal_unblock(curthread); 399a091d823SDavid Xu } 400a091d823SDavid Xu 401a091d823SDavid Xu void 4028495e8b1SKonstantin Belousov _thr_signal_init(int dlopened) 403a091d823SDavid Xu { 4048495e8b1SKonstantin Belousov struct sigaction act, nact, oact; 4058495e8b1SKonstantin Belousov struct usigaction *usa; 4068495e8b1SKonstantin Belousov sigset_t oldset; 4078495e8b1SKonstantin Belousov int sig, error; 4088495e8b1SKonstantin Belousov 4098495e8b1SKonstantin Belousov if (dlopened) { 4108495e8b1SKonstantin Belousov __sys_sigprocmask(SIG_SETMASK, &_thr_maskset, &oldset); 4118495e8b1SKonstantin Belousov for (sig = 1; sig <= _SIG_MAXSIG; sig++) { 4128495e8b1SKonstantin Belousov if (sig == SIGCANCEL) 4138495e8b1SKonstantin Belousov continue; 4148495e8b1SKonstantin Belousov error = __sys_sigaction(sig, NULL, &oact); 4158495e8b1SKonstantin Belousov if (error == -1 || oact.sa_handler == SIG_DFL || 4168495e8b1SKonstantin Belousov oact.sa_handler == SIG_IGN) 4178495e8b1SKonstantin Belousov continue; 4188495e8b1SKonstantin Belousov usa = __libc_sigaction_slot(sig); 4198495e8b1SKonstantin Belousov usa->sigact = oact; 4208495e8b1SKonstantin Belousov nact = oact; 4218495e8b1SKonstantin Belousov remove_thr_signals(&usa->sigact.sa_mask); 4228495e8b1SKonstantin Belousov nact.sa_flags &= ~SA_NODEFER; 4238495e8b1SKonstantin Belousov nact.sa_flags |= SA_SIGINFO; 4248495e8b1SKonstantin Belousov nact.sa_sigaction = thr_sighandler; 4258495e8b1SKonstantin Belousov nact.sa_mask = _thr_maskset; 4268495e8b1SKonstantin Belousov (void)__sys_sigaction(sig, &nact, NULL); 4278495e8b1SKonstantin Belousov } 4288495e8b1SKonstantin Belousov __sys_sigprocmask(SIG_SETMASK, &oldset, NULL); 4298495e8b1SKonstantin Belousov } 430a091d823SDavid Xu 43102c3c858SDavid Xu /* Install SIGCANCEL handler. */ 43202c3c858SDavid Xu SIGFILLSET(act.sa_mask); 43302c3c858SDavid Xu act.sa_flags = SA_SIGINFO; 434a091d823SDavid Xu act.sa_sigaction = (__siginfohandler_t *)&sigcancel_handler; 435a091d823SDavid Xu __sys_sigaction(SIGCANCEL, &act, NULL); 43602c3c858SDavid Xu 43702c3c858SDavid Xu /* Unblock SIGCANCEL */ 43802c3c858SDavid Xu SIGEMPTYSET(act.sa_mask); 43902c3c858SDavid Xu SIGADDSET(act.sa_mask, SIGCANCEL); 44002c3c858SDavid Xu __sys_sigprocmask(SIG_UNBLOCK, &act.sa_mask, NULL); 44102c3c858SDavid Xu } 44202c3c858SDavid Xu 44302c3c858SDavid Xu void 44402c3c858SDavid Xu _thr_sigact_unload(struct dl_phdr_info *phdr_info) 44502c3c858SDavid Xu { 446cb4a1047SDavid Xu #if 0 44721a9296fSDavid Xu struct pthread *curthread = _get_curthread(); 44802c3c858SDavid Xu struct urwlock *rwlp; 44902c3c858SDavid Xu struct sigaction *actp; 4508495e8b1SKonstantin Belousov struct usigaction *usa; 45102c3c858SDavid Xu struct sigaction kact; 45202c3c858SDavid Xu void (*handler)(int); 45302c3c858SDavid Xu int sig; 45402c3c858SDavid Xu 45521a9296fSDavid Xu _thr_signal_block(curthread); 456cb4a1047SDavid Xu for (sig = 1; sig <= _SIG_MAXSIG; sig++) { 4578495e8b1SKonstantin Belousov usa = __libc_sigaction_slot(sig); 4588495e8b1SKonstantin Belousov actp = &usa->sigact; 45902c3c858SDavid Xu retry: 46002c3c858SDavid Xu handler = actp->sa_handler; 46102c3c858SDavid Xu if (handler != SIG_DFL && handler != SIG_IGN && 46202c3c858SDavid Xu __elf_phdr_match_addr(phdr_info, handler)) { 4638495e8b1SKonstantin Belousov rwlp = &usa->lock; 46402c3c858SDavid Xu _thr_rwl_wrlock(rwlp); 46502c3c858SDavid Xu if (handler != actp->sa_handler) { 46602c3c858SDavid Xu _thr_rwl_unlock(rwlp); 46702c3c858SDavid Xu goto retry; 46802c3c858SDavid Xu } 46902c3c858SDavid Xu actp->sa_handler = SIG_DFL; 47002c3c858SDavid Xu actp->sa_flags = SA_SIGINFO; 47102c3c858SDavid Xu SIGEMPTYSET(actp->sa_mask); 47202c3c858SDavid Xu if (__sys_sigaction(sig, NULL, &kact) == 0 && 47302c3c858SDavid Xu kact.sa_handler != SIG_DFL && 47402c3c858SDavid Xu kact.sa_handler != SIG_IGN) 47502c3c858SDavid Xu __sys_sigaction(sig, actp, NULL); 47602c3c858SDavid Xu _thr_rwl_unlock(rwlp); 47702c3c858SDavid Xu } 47802c3c858SDavid Xu } 47921a9296fSDavid Xu _thr_signal_unblock(curthread); 480cb4a1047SDavid Xu #endif 48102c3c858SDavid Xu } 48202c3c858SDavid Xu 48302c3c858SDavid Xu void 48402c3c858SDavid Xu _thr_signal_prefork(void) 48502c3c858SDavid Xu { 48602c3c858SDavid Xu int i; 48702c3c858SDavid Xu 48891792417SJilles Tjoelker for (i = 1; i <= _SIG_MAXSIG; ++i) 4898495e8b1SKonstantin Belousov _thr_rwl_rdlock(&__libc_sigaction_slot(i)->lock); 49002c3c858SDavid Xu } 49102c3c858SDavid Xu 49202c3c858SDavid Xu void 49302c3c858SDavid Xu _thr_signal_postfork(void) 49402c3c858SDavid Xu { 49502c3c858SDavid Xu int i; 49602c3c858SDavid Xu 49791792417SJilles Tjoelker for (i = 1; i <= _SIG_MAXSIG; ++i) 4988495e8b1SKonstantin Belousov _thr_rwl_unlock(&__libc_sigaction_slot(i)->lock); 49902c3c858SDavid Xu } 50002c3c858SDavid Xu 50102c3c858SDavid Xu void 50202c3c858SDavid Xu _thr_signal_postfork_child(void) 50302c3c858SDavid Xu { 50402c3c858SDavid Xu int i; 50502c3c858SDavid Xu 5068495e8b1SKonstantin Belousov for (i = 1; i <= _SIG_MAXSIG; ++i) { 5078495e8b1SKonstantin Belousov bzero(&__libc_sigaction_slot(i) -> lock, 5088495e8b1SKonstantin Belousov sizeof(struct urwlock)); 5098495e8b1SKonstantin Belousov } 510a091d823SDavid Xu } 511a091d823SDavid Xu 512a091d823SDavid Xu void 513a091d823SDavid Xu _thr_signal_deinit(void) 514a091d823SDavid Xu { 515a091d823SDavid Xu } 516a091d823SDavid Xu 51705c3a5eaSDavid Xu int 5188495e8b1SKonstantin Belousov __thr_sigaction(int sig, const struct sigaction *act, struct sigaction *oact) 519a091d823SDavid Xu { 52002c3c858SDavid Xu struct sigaction newact, oldact, oldact2; 52102c3c858SDavid Xu sigset_t oldset; 5228495e8b1SKonstantin Belousov struct usigaction *usa; 5238495e8b1SKonstantin Belousov int ret, err; 52402c3c858SDavid Xu 525b144e48bSKonstantin Belousov if (!_SIG_VALID(sig) || sig == SIGCANCEL) { 526a091d823SDavid Xu errno = EINVAL; 527a091d823SDavid Xu return (-1); 528a091d823SDavid Xu } 529a091d823SDavid Xu 5308495e8b1SKonstantin Belousov ret = 0; 5318495e8b1SKonstantin Belousov err = 0; 5328495e8b1SKonstantin Belousov usa = __libc_sigaction_slot(sig); 53302c3c858SDavid Xu 53402c3c858SDavid Xu __sys_sigprocmask(SIG_SETMASK, &_thr_maskset, &oldset); 5358495e8b1SKonstantin Belousov _thr_rwl_wrlock(&usa->lock); 53602c3c858SDavid Xu 53702c3c858SDavid Xu if (act != NULL) { 5388495e8b1SKonstantin Belousov oldact2 = usa->sigact; 5398495e8b1SKonstantin Belousov newact = *act; 54002c3c858SDavid Xu 54102c3c858SDavid Xu /* 54202c3c858SDavid Xu * if a new sig handler is SIG_DFL or SIG_IGN, 5438495e8b1SKonstantin Belousov * don't remove old handler from __libc_sigact[], 54402c3c858SDavid Xu * so deferred signals still can use the handlers, 54502c3c858SDavid Xu * multiple threads invoking sigaction itself is 54602c3c858SDavid Xu * a race condition, so it is not a problem. 54702c3c858SDavid Xu */ 54802c3c858SDavid Xu if (newact.sa_handler != SIG_DFL && 54902c3c858SDavid Xu newact.sa_handler != SIG_IGN) { 5508495e8b1SKonstantin Belousov usa->sigact = newact; 5518495e8b1SKonstantin Belousov remove_thr_signals(&usa->sigact.sa_mask); 55202c3c858SDavid Xu newact.sa_flags &= ~SA_NODEFER; 55302c3c858SDavid Xu newact.sa_flags |= SA_SIGINFO; 55402c3c858SDavid Xu newact.sa_sigaction = thr_sighandler; 55502c3c858SDavid Xu newact.sa_mask = _thr_maskset; /* mask all signals */ 55602c3c858SDavid Xu } 5578495e8b1SKonstantin Belousov ret = __sys_sigaction(sig, &newact, &oldact); 5588495e8b1SKonstantin Belousov if (ret == -1) { 55902c3c858SDavid Xu err = errno; 5608495e8b1SKonstantin Belousov usa->sigact = oldact2; 56102c3c858SDavid Xu } 56202c3c858SDavid Xu } else if (oact != NULL) { 56302c3c858SDavid Xu ret = __sys_sigaction(sig, NULL, &oldact); 56402c3c858SDavid Xu err = errno; 56502c3c858SDavid Xu } 56602c3c858SDavid Xu 5678495e8b1SKonstantin Belousov if (oldact.sa_handler != SIG_DFL && oldact.sa_handler != SIG_IGN) { 5686ed79f06SDavid Xu if (act != NULL) 5696ed79f06SDavid Xu oldact = oldact2; 5706ed79f06SDavid Xu else if (oact != NULL) 5718495e8b1SKonstantin Belousov oldact = usa->sigact; 57202c3c858SDavid Xu } 57302c3c858SDavid Xu 5748495e8b1SKonstantin Belousov _thr_rwl_unlock(&usa->lock); 57502c3c858SDavid Xu __sys_sigprocmask(SIG_SETMASK, &oldset, NULL); 57602c3c858SDavid Xu 57702c3c858SDavid Xu if (ret == 0) { 57802c3c858SDavid Xu if (oact != NULL) 57902c3c858SDavid Xu *oact = oldact; 58002c3c858SDavid Xu } else { 58102c3c858SDavid Xu errno = err; 58202c3c858SDavid Xu } 58302c3c858SDavid Xu return (ret); 584a091d823SDavid Xu } 585a091d823SDavid Xu 586a091d823SDavid Xu int 5878495e8b1SKonstantin Belousov __thr_sigprocmask(int how, const sigset_t *set, sigset_t *oset) 588a091d823SDavid Xu { 589a091d823SDavid Xu const sigset_t *p = set; 590a091d823SDavid Xu sigset_t newset; 591a091d823SDavid Xu 592a091d823SDavid Xu if (how != SIG_UNBLOCK) { 593a091d823SDavid Xu if (set != NULL) { 594a091d823SDavid Xu newset = *set; 595a091d823SDavid Xu SIGDELSET(newset, SIGCANCEL); 596a091d823SDavid Xu p = &newset; 597a091d823SDavid Xu } 598a091d823SDavid Xu } 599a091d823SDavid Xu return (__sys_sigprocmask(how, p, oset)); 600a091d823SDavid Xu } 601a091d823SDavid Xu 602bb535300SJeff Roberson __weak_reference(_pthread_sigmask, pthread_sigmask); 603bb535300SJeff Roberson 604bb535300SJeff Roberson int 605bb535300SJeff Roberson _pthread_sigmask(int how, const sigset_t *set, sigset_t *oset) 606bb535300SJeff Roberson { 6079e8bff64SKonstantin Belousov 6089e8bff64SKonstantin Belousov if (__thr_sigprocmask(how, set, oset)) 609a091d823SDavid Xu return (errno); 610a091d823SDavid Xu return (0); 611bb535300SJeff Roberson } 612bb535300SJeff Roberson 613bb535300SJeff Roberson int 614a091d823SDavid Xu _sigsuspend(const sigset_t * set) 615bb535300SJeff Roberson { 61605c3a5eaSDavid Xu sigset_t newset; 61705c3a5eaSDavid Xu 61882746ea5SDavid Xu return (__sys_sigsuspend(thr_remove_thr_signals(set, &newset))); 61905c3a5eaSDavid Xu } 62005c3a5eaSDavid Xu 62105c3a5eaSDavid Xu int 6228495e8b1SKonstantin Belousov __thr_sigsuspend(const sigset_t * set) 62305c3a5eaSDavid Xu { 62402c3c858SDavid Xu struct pthread *curthread; 625a091d823SDavid Xu sigset_t newset; 62602c3c858SDavid Xu int ret, old; 627bb535300SJeff Roberson 62802c3c858SDavid Xu curthread = _get_curthread(); 62902c3c858SDavid Xu 63002c3c858SDavid Xu old = curthread->in_sigsuspend; 63102c3c858SDavid Xu curthread->in_sigsuspend = 1; 632f08e1bf6SDavid Xu _thr_cancel_enter(curthread); 63382746ea5SDavid Xu ret = __sys_sigsuspend(thr_remove_thr_signals(set, &newset)); 63402c3c858SDavid Xu _thr_cancel_leave(curthread, 1); 63502c3c858SDavid Xu curthread->in_sigsuspend = old; 63602c3c858SDavid Xu if (curthread->unblock_sigcancel) { 63702c3c858SDavid Xu curthread->unblock_sigcancel = 0; 63802c3c858SDavid Xu SIGEMPTYSET(newset); 63902c3c858SDavid Xu SIGADDSET(newset, SIGCANCEL); 64002c3c858SDavid Xu __sys_sigprocmask(SIG_UNBLOCK, &newset, NULL); 64102c3c858SDavid Xu } 6420ad70ba9SMike Makonnen 643a091d823SDavid Xu return (ret); 644a091d823SDavid Xu } 645a091d823SDavid Xu 646a091d823SDavid Xu int 64705c3a5eaSDavid Xu _sigtimedwait(const sigset_t *set, siginfo_t *info, 64805c3a5eaSDavid Xu const struct timespec * timeout) 64905c3a5eaSDavid Xu { 65005c3a5eaSDavid Xu sigset_t newset; 65105c3a5eaSDavid Xu 65282746ea5SDavid Xu return (__sys_sigtimedwait(thr_remove_thr_signals(set, &newset), info, 65382746ea5SDavid Xu timeout)); 65405c3a5eaSDavid Xu } 65505c3a5eaSDavid Xu 656635f917aSDavid Xu /* 657635f917aSDavid Xu * Cancellation behavior: 658635f917aSDavid Xu * Thread may be canceled at start, if thread got signal, 659635f917aSDavid Xu * it is not canceled. 660635f917aSDavid Xu */ 66105c3a5eaSDavid Xu int 6628495e8b1SKonstantin Belousov __thr_sigtimedwait(const sigset_t *set, siginfo_t *info, 663a091d823SDavid Xu const struct timespec * timeout) 664a091d823SDavid Xu { 665a091d823SDavid Xu struct pthread *curthread = _get_curthread(); 666a091d823SDavid Xu sigset_t newset; 667a091d823SDavid Xu int ret; 668a091d823SDavid Xu 66902c3c858SDavid Xu _thr_cancel_enter(curthread); 67082746ea5SDavid Xu ret = __sys_sigtimedwait(thr_remove_thr_signals(set, &newset), info, 67182746ea5SDavid Xu timeout); 67202c3c858SDavid Xu _thr_cancel_leave(curthread, (ret == -1)); 673a091d823SDavid Xu return (ret); 674a091d823SDavid Xu } 675a091d823SDavid Xu 676a091d823SDavid Xu int 67705c3a5eaSDavid Xu _sigwaitinfo(const sigset_t *set, siginfo_t *info) 67805c3a5eaSDavid Xu { 67905c3a5eaSDavid Xu sigset_t newset; 68005c3a5eaSDavid Xu 68182746ea5SDavid Xu return (__sys_sigwaitinfo(thr_remove_thr_signals(set, &newset), info)); 68205c3a5eaSDavid Xu } 68305c3a5eaSDavid Xu 684635f917aSDavid Xu /* 685635f917aSDavid Xu * Cancellation behavior: 686635f917aSDavid Xu * Thread may be canceled at start, if thread got signal, 687635f917aSDavid Xu * it is not canceled. 688635f917aSDavid Xu */ 68905c3a5eaSDavid Xu int 6908495e8b1SKonstantin Belousov __thr_sigwaitinfo(const sigset_t *set, siginfo_t *info) 691a091d823SDavid Xu { 692a091d823SDavid Xu struct pthread *curthread = _get_curthread(); 693a091d823SDavid Xu sigset_t newset; 694a091d823SDavid Xu int ret; 695a091d823SDavid Xu 69602c3c858SDavid Xu _thr_cancel_enter(curthread); 69782746ea5SDavid Xu ret = __sys_sigwaitinfo(thr_remove_thr_signals(set, &newset), info); 69802c3c858SDavid Xu _thr_cancel_leave(curthread, ret == -1); 699a091d823SDavid Xu return (ret); 700a091d823SDavid Xu } 701a091d823SDavid Xu 702a091d823SDavid Xu int 70305c3a5eaSDavid Xu _sigwait(const sigset_t *set, int *sig) 70405c3a5eaSDavid Xu { 70505c3a5eaSDavid Xu sigset_t newset; 70605c3a5eaSDavid Xu 70782746ea5SDavid Xu return (__sys_sigwait(thr_remove_thr_signals(set, &newset), sig)); 70805c3a5eaSDavid Xu } 70905c3a5eaSDavid Xu 710635f917aSDavid Xu /* 711635f917aSDavid Xu * Cancellation behavior: 712635f917aSDavid Xu * Thread may be canceled at start, if thread got signal, 713635f917aSDavid Xu * it is not canceled. 714635f917aSDavid Xu */ 71505c3a5eaSDavid Xu int 7168495e8b1SKonstantin Belousov __thr_sigwait(const sigset_t *set, int *sig) 717a091d823SDavid Xu { 718a091d823SDavid Xu struct pthread *curthread = _get_curthread(); 719a091d823SDavid Xu sigset_t newset; 720a091d823SDavid Xu int ret; 721a091d823SDavid Xu 72283c9e089SDavid Xu do { 72302c3c858SDavid Xu _thr_cancel_enter(curthread); 72482746ea5SDavid Xu ret = __sys_sigwait(thr_remove_thr_signals(set, &newset), sig); 72502c3c858SDavid Xu _thr_cancel_leave(curthread, (ret != 0)); 72683c9e089SDavid Xu } while (ret == EINTR); 727a091d823SDavid Xu return (ret); 728bb535300SJeff Roberson } 7295cf22195SDavid Xu 7305cf22195SDavid Xu int 7318495e8b1SKonstantin Belousov __thr_setcontext(const ucontext_t *ucp) 7325cf22195SDavid Xu { 7335cf22195SDavid Xu ucontext_t uc; 7345cf22195SDavid Xu 73566f6c272SDavid Xu if (ucp == NULL) { 73666f6c272SDavid Xu errno = EINVAL; 73766f6c272SDavid Xu return (-1); 73866f6c272SDavid Xu } 7398bbeb7e9SDavid Xu if (!SIGISMEMBER(uc.uc_sigmask, SIGCANCEL)) 7408bbeb7e9SDavid Xu return __sys_setcontext(ucp); 7415cf22195SDavid Xu (void) memcpy(&uc, ucp, sizeof(uc)); 7428bbeb7e9SDavid Xu SIGDELSET(uc.uc_sigmask, SIGCANCEL); 7438495e8b1SKonstantin Belousov return (__sys_setcontext(&uc)); 7445cf22195SDavid Xu } 7455cf22195SDavid Xu 746acad2b1eSDavid Xu int 7478495e8b1SKonstantin Belousov __thr_swapcontext(ucontext_t *oucp, const ucontext_t *ucp) 748acad2b1eSDavid Xu { 749acad2b1eSDavid Xu ucontext_t uc; 750acad2b1eSDavid Xu 75166f6c272SDavid Xu if (oucp == NULL || ucp == NULL) { 75266f6c272SDavid Xu errno = EINVAL; 75366f6c272SDavid Xu return (-1); 75466f6c272SDavid Xu } 7558bbeb7e9SDavid Xu if (SIGISMEMBER(ucp->uc_sigmask, SIGCANCEL)) { 756acad2b1eSDavid Xu (void) memcpy(&uc, ucp, sizeof(uc)); 7578bbeb7e9SDavid Xu SIGDELSET(uc.uc_sigmask, SIGCANCEL); 7588bbeb7e9SDavid Xu ucp = &uc; 7598bbeb7e9SDavid Xu } 7608495e8b1SKonstantin Belousov return (__sys_swapcontext(oucp, ucp)); 761acad2b1eSDavid Xu } 762