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 41*8495e8b1SKonstantin 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 58*8495e8b1SKonstantin Belousov static inline struct usigaction * 59*8495e8b1SKonstantin Belousov __libc_sigaction_slot(int signo) 60*8495e8b1SKonstantin Belousov { 61*8495e8b1SKonstantin Belousov 62*8495e8b1SKonstantin Belousov return (&_thr_sigact[signo - 1]); 63*8495e8b1SKonstantin Belousov } 64*8495e8b1SKonstantin 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 146*8495e8b1SKonstantin Belousov typedef void (*ohandler)(int sig, int code, struct sigcontext *scp, 147*8495e8b1SKonstantin 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 { 155*8495e8b1SKonstantin Belousov struct pthread *curthread; 156*8495e8b1SKonstantin Belousov ucontext_t *ucp; 15702c3c858SDavid Xu struct sigaction act; 158*8495e8b1SKonstantin Belousov struct usigaction *usa; 15902c3c858SDavid Xu int err; 16002c3c858SDavid Xu 16102c3c858SDavid Xu err = errno; 162*8495e8b1SKonstantin Belousov curthread = _get_curthread(); 163*8495e8b1SKonstantin Belousov ucp = _ucp; 164*8495e8b1SKonstantin Belousov usa = __libc_sigaction_slot(sig); 165*8495e8b1SKonstantin Belousov _thr_rwl_rdlock(&usa->lock); 166*8495e8b1SKonstantin Belousov act = usa->sigact; 167*8495e8b1SKonstantin 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 */ 241*8495e8b1SKonstantin Belousov if ((actp->sa_flags & SA_SIGINFO) != 0) { 242*8495e8b1SKonstantin Belousov sigfunc(sig, info, ucp); 243*8495e8b1SKonstantin Belousov } else { 244*8495e8b1SKonstantin Belousov ((ohandler)sigfunc)(sig, info->si_code, 245*8495e8b1SKonstantin Belousov (struct sigcontext *)ucp, info->si_addr, 246*8495e8b1SKonstantin 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 296635f917aSDavid Xu * is used, it is mostly caused my an unresolved PLT. 297635f917aSDavid Xu * 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 402*8495e8b1SKonstantin Belousov _thr_signal_init(int dlopened) 403a091d823SDavid Xu { 404*8495e8b1SKonstantin Belousov struct sigaction act, nact, oact; 405*8495e8b1SKonstantin Belousov struct usigaction *usa; 406*8495e8b1SKonstantin Belousov sigset_t oldset; 407*8495e8b1SKonstantin Belousov int sig, error; 408*8495e8b1SKonstantin Belousov 409*8495e8b1SKonstantin Belousov if (dlopened) { 410*8495e8b1SKonstantin Belousov __sys_sigprocmask(SIG_SETMASK, &_thr_maskset, &oldset); 411*8495e8b1SKonstantin Belousov for (sig = 1; sig <= _SIG_MAXSIG; sig++) { 412*8495e8b1SKonstantin Belousov if (sig == SIGCANCEL) 413*8495e8b1SKonstantin Belousov continue; 414*8495e8b1SKonstantin Belousov error = __sys_sigaction(sig, NULL, &oact); 415*8495e8b1SKonstantin Belousov if (error == -1 || oact.sa_handler == SIG_DFL || 416*8495e8b1SKonstantin Belousov oact.sa_handler == SIG_IGN) 417*8495e8b1SKonstantin Belousov continue; 418*8495e8b1SKonstantin Belousov usa = __libc_sigaction_slot(sig); 419*8495e8b1SKonstantin Belousov usa->sigact = oact; 420*8495e8b1SKonstantin Belousov nact = oact; 421*8495e8b1SKonstantin Belousov remove_thr_signals(&usa->sigact.sa_mask); 422*8495e8b1SKonstantin Belousov nact.sa_flags &= ~SA_NODEFER; 423*8495e8b1SKonstantin Belousov nact.sa_flags |= SA_SIGINFO; 424*8495e8b1SKonstantin Belousov nact.sa_sigaction = thr_sighandler; 425*8495e8b1SKonstantin Belousov nact.sa_mask = _thr_maskset; 426*8495e8b1SKonstantin Belousov (void)__sys_sigaction(sig, &nact, NULL); 427*8495e8b1SKonstantin Belousov } 428*8495e8b1SKonstantin Belousov __sys_sigprocmask(SIG_SETMASK, &oldset, NULL); 429*8495e8b1SKonstantin 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; 450*8495e8b1SKonstantin 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++) { 457*8495e8b1SKonstantin Belousov usa = __libc_sigaction_slot(sig); 458*8495e8b1SKonstantin 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)) { 463*8495e8b1SKonstantin 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) 489*8495e8b1SKonstantin 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) 498*8495e8b1SKonstantin 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 506*8495e8b1SKonstantin Belousov for (i = 1; i <= _SIG_MAXSIG; ++i) { 507*8495e8b1SKonstantin Belousov bzero(&__libc_sigaction_slot(i) -> lock, 508*8495e8b1SKonstantin Belousov sizeof(struct urwlock)); 509*8495e8b1SKonstantin Belousov } 510a091d823SDavid Xu } 511a091d823SDavid Xu 512a091d823SDavid Xu void 513a091d823SDavid Xu _thr_signal_deinit(void) 514a091d823SDavid Xu { 515a091d823SDavid Xu } 516a091d823SDavid Xu 51705c3a5eaSDavid Xu int 518*8495e8b1SKonstantin Belousov __thr_pause(void) 51905c3a5eaSDavid Xu { 52002c3c858SDavid Xu sigset_t oset; 52105c3a5eaSDavid Xu 52202c3c858SDavid Xu if (_sigprocmask(SIG_BLOCK, NULL, &oset) == -1) 52302c3c858SDavid Xu return (-1); 524*8495e8b1SKonstantin Belousov return (__thr_sigsuspend(&oset)); 52505c3a5eaSDavid Xu } 52605c3a5eaSDavid Xu 52705c3a5eaSDavid Xu int 528*8495e8b1SKonstantin Belousov __thr_raise(int sig) 52905c3a5eaSDavid Xu { 530*8495e8b1SKonstantin Belousov 531*8495e8b1SKonstantin Belousov return (_thr_send_sig(_get_curthread(), sig)); 53205c3a5eaSDavid Xu } 53305c3a5eaSDavid Xu 534a091d823SDavid Xu int 535*8495e8b1SKonstantin Belousov __thr_sigaction(int sig, const struct sigaction *act, struct sigaction *oact) 536a091d823SDavid Xu { 53702c3c858SDavid Xu struct sigaction newact, oldact, oldact2; 53802c3c858SDavid Xu sigset_t oldset; 539*8495e8b1SKonstantin Belousov struct usigaction *usa; 540*8495e8b1SKonstantin Belousov int ret, err; 54102c3c858SDavid Xu 542b144e48bSKonstantin Belousov if (!_SIG_VALID(sig) || sig == SIGCANCEL) { 543a091d823SDavid Xu errno = EINVAL; 544a091d823SDavid Xu return (-1); 545a091d823SDavid Xu } 546a091d823SDavid Xu 547*8495e8b1SKonstantin Belousov ret = 0; 548*8495e8b1SKonstantin Belousov err = 0; 549*8495e8b1SKonstantin Belousov usa = __libc_sigaction_slot(sig); 55002c3c858SDavid Xu 55102c3c858SDavid Xu __sys_sigprocmask(SIG_SETMASK, &_thr_maskset, &oldset); 552*8495e8b1SKonstantin Belousov _thr_rwl_wrlock(&usa->lock); 55302c3c858SDavid Xu 55402c3c858SDavid Xu if (act != NULL) { 555*8495e8b1SKonstantin Belousov oldact2 = usa->sigact; 556*8495e8b1SKonstantin Belousov newact = *act; 55702c3c858SDavid Xu 55802c3c858SDavid Xu /* 55902c3c858SDavid Xu * if a new sig handler is SIG_DFL or SIG_IGN, 560*8495e8b1SKonstantin Belousov * don't remove old handler from __libc_sigact[], 56102c3c858SDavid Xu * so deferred signals still can use the handlers, 56202c3c858SDavid Xu * multiple threads invoking sigaction itself is 56302c3c858SDavid Xu * a race condition, so it is not a problem. 56402c3c858SDavid Xu */ 56502c3c858SDavid Xu if (newact.sa_handler != SIG_DFL && 56602c3c858SDavid Xu newact.sa_handler != SIG_IGN) { 567*8495e8b1SKonstantin Belousov usa->sigact = newact; 568*8495e8b1SKonstantin Belousov remove_thr_signals(&usa->sigact.sa_mask); 56902c3c858SDavid Xu newact.sa_flags &= ~SA_NODEFER; 57002c3c858SDavid Xu newact.sa_flags |= SA_SIGINFO; 57102c3c858SDavid Xu newact.sa_sigaction = thr_sighandler; 57202c3c858SDavid Xu newact.sa_mask = _thr_maskset; /* mask all signals */ 57302c3c858SDavid Xu } 574*8495e8b1SKonstantin Belousov ret = __sys_sigaction(sig, &newact, &oldact); 575*8495e8b1SKonstantin Belousov if (ret == -1) { 57602c3c858SDavid Xu err = errno; 577*8495e8b1SKonstantin Belousov usa->sigact = oldact2; 57802c3c858SDavid Xu } 57902c3c858SDavid Xu } else if (oact != NULL) { 58002c3c858SDavid Xu ret = __sys_sigaction(sig, NULL, &oldact); 58102c3c858SDavid Xu err = errno; 58202c3c858SDavid Xu } 58302c3c858SDavid Xu 584*8495e8b1SKonstantin Belousov if (oldact.sa_handler != SIG_DFL && oldact.sa_handler != SIG_IGN) { 5856ed79f06SDavid Xu if (act != NULL) 5866ed79f06SDavid Xu oldact = oldact2; 5876ed79f06SDavid Xu else if (oact != NULL) 588*8495e8b1SKonstantin Belousov oldact = usa->sigact; 58902c3c858SDavid Xu } 59002c3c858SDavid Xu 591*8495e8b1SKonstantin Belousov _thr_rwl_unlock(&usa->lock); 59202c3c858SDavid Xu __sys_sigprocmask(SIG_SETMASK, &oldset, NULL); 59302c3c858SDavid Xu 59402c3c858SDavid Xu if (ret == 0) { 59502c3c858SDavid Xu if (oact != NULL) 59602c3c858SDavid Xu *oact = oldact; 59702c3c858SDavid Xu } else { 59802c3c858SDavid Xu errno = err; 59902c3c858SDavid Xu } 60002c3c858SDavid Xu return (ret); 601a091d823SDavid Xu } 602a091d823SDavid Xu 603a091d823SDavid Xu int 604*8495e8b1SKonstantin Belousov __thr_sigprocmask(int how, const sigset_t *set, sigset_t *oset) 605a091d823SDavid Xu { 606a091d823SDavid Xu const sigset_t *p = set; 607a091d823SDavid Xu sigset_t newset; 608a091d823SDavid Xu 609a091d823SDavid Xu if (how != SIG_UNBLOCK) { 610a091d823SDavid Xu if (set != NULL) { 611a091d823SDavid Xu newset = *set; 612a091d823SDavid Xu SIGDELSET(newset, SIGCANCEL); 613a091d823SDavid Xu p = &newset; 614a091d823SDavid Xu } 615a091d823SDavid Xu } 616a091d823SDavid Xu return (__sys_sigprocmask(how, p, oset)); 617a091d823SDavid Xu } 618a091d823SDavid Xu 619bb535300SJeff Roberson __weak_reference(_pthread_sigmask, pthread_sigmask); 620bb535300SJeff Roberson 621bb535300SJeff Roberson int 622bb535300SJeff Roberson _pthread_sigmask(int how, const sigset_t *set, sigset_t *oset) 623bb535300SJeff Roberson { 624a091d823SDavid Xu if (_sigprocmask(how, set, oset)) 625a091d823SDavid Xu return (errno); 626a091d823SDavid Xu return (0); 627bb535300SJeff Roberson } 628bb535300SJeff Roberson 629bb535300SJeff Roberson int 630a091d823SDavid Xu _sigsuspend(const sigset_t * set) 631bb535300SJeff Roberson { 63205c3a5eaSDavid Xu sigset_t newset; 63305c3a5eaSDavid Xu 63482746ea5SDavid Xu return (__sys_sigsuspend(thr_remove_thr_signals(set, &newset))); 63505c3a5eaSDavid Xu } 63605c3a5eaSDavid Xu 63705c3a5eaSDavid Xu int 638*8495e8b1SKonstantin Belousov __thr_sigsuspend(const sigset_t * set) 63905c3a5eaSDavid Xu { 64002c3c858SDavid Xu struct pthread *curthread; 641a091d823SDavid Xu sigset_t newset; 64202c3c858SDavid Xu int ret, old; 643bb535300SJeff Roberson 64402c3c858SDavid Xu curthread = _get_curthread(); 64502c3c858SDavid Xu 64602c3c858SDavid Xu old = curthread->in_sigsuspend; 64702c3c858SDavid Xu curthread->in_sigsuspend = 1; 648f08e1bf6SDavid Xu _thr_cancel_enter(curthread); 64982746ea5SDavid Xu ret = __sys_sigsuspend(thr_remove_thr_signals(set, &newset)); 65002c3c858SDavid Xu _thr_cancel_leave(curthread, 1); 65102c3c858SDavid Xu curthread->in_sigsuspend = old; 65202c3c858SDavid Xu if (curthread->unblock_sigcancel) { 65302c3c858SDavid Xu curthread->unblock_sigcancel = 0; 65402c3c858SDavid Xu SIGEMPTYSET(newset); 65502c3c858SDavid Xu SIGADDSET(newset, SIGCANCEL); 65602c3c858SDavid Xu __sys_sigprocmask(SIG_UNBLOCK, &newset, NULL); 65702c3c858SDavid Xu } 6580ad70ba9SMike Makonnen 659a091d823SDavid Xu return (ret); 660a091d823SDavid Xu } 661a091d823SDavid Xu 662a091d823SDavid Xu int 66305c3a5eaSDavid Xu _sigtimedwait(const sigset_t *set, siginfo_t *info, 66405c3a5eaSDavid Xu const struct timespec * timeout) 66505c3a5eaSDavid Xu { 66605c3a5eaSDavid Xu sigset_t newset; 66705c3a5eaSDavid Xu 66882746ea5SDavid Xu return (__sys_sigtimedwait(thr_remove_thr_signals(set, &newset), info, 66982746ea5SDavid Xu timeout)); 67005c3a5eaSDavid Xu } 67105c3a5eaSDavid Xu 672635f917aSDavid Xu /* 673635f917aSDavid Xu * Cancellation behavior: 674635f917aSDavid Xu * Thread may be canceled at start, if thread got signal, 675635f917aSDavid Xu * it is not canceled. 676635f917aSDavid Xu */ 67705c3a5eaSDavid Xu int 678*8495e8b1SKonstantin Belousov __thr_sigtimedwait(const sigset_t *set, siginfo_t *info, 679a091d823SDavid Xu const struct timespec * timeout) 680a091d823SDavid Xu { 681a091d823SDavid Xu struct pthread *curthread = _get_curthread(); 682a091d823SDavid Xu sigset_t newset; 683a091d823SDavid Xu int ret; 684a091d823SDavid Xu 68502c3c858SDavid Xu _thr_cancel_enter(curthread); 68682746ea5SDavid Xu ret = __sys_sigtimedwait(thr_remove_thr_signals(set, &newset), info, 68782746ea5SDavid Xu timeout); 68802c3c858SDavid Xu _thr_cancel_leave(curthread, (ret == -1)); 689a091d823SDavid Xu return (ret); 690a091d823SDavid Xu } 691a091d823SDavid Xu 692a091d823SDavid Xu int 69305c3a5eaSDavid Xu _sigwaitinfo(const sigset_t *set, siginfo_t *info) 69405c3a5eaSDavid Xu { 69505c3a5eaSDavid Xu sigset_t newset; 69605c3a5eaSDavid Xu 69782746ea5SDavid Xu return (__sys_sigwaitinfo(thr_remove_thr_signals(set, &newset), info)); 69805c3a5eaSDavid Xu } 69905c3a5eaSDavid Xu 700635f917aSDavid Xu /* 701635f917aSDavid Xu * Cancellation behavior: 702635f917aSDavid Xu * Thread may be canceled at start, if thread got signal, 703635f917aSDavid Xu * it is not canceled. 704635f917aSDavid Xu */ 70505c3a5eaSDavid Xu int 706*8495e8b1SKonstantin Belousov __thr_sigwaitinfo(const sigset_t *set, siginfo_t *info) 707a091d823SDavid Xu { 708a091d823SDavid Xu struct pthread *curthread = _get_curthread(); 709a091d823SDavid Xu sigset_t newset; 710a091d823SDavid Xu int ret; 711a091d823SDavid Xu 71202c3c858SDavid Xu _thr_cancel_enter(curthread); 71382746ea5SDavid Xu ret = __sys_sigwaitinfo(thr_remove_thr_signals(set, &newset), info); 71402c3c858SDavid Xu _thr_cancel_leave(curthread, ret == -1); 715a091d823SDavid Xu return (ret); 716a091d823SDavid Xu } 717a091d823SDavid Xu 718a091d823SDavid Xu int 71905c3a5eaSDavid Xu _sigwait(const sigset_t *set, int *sig) 72005c3a5eaSDavid Xu { 72105c3a5eaSDavid Xu sigset_t newset; 72205c3a5eaSDavid Xu 72382746ea5SDavid Xu return (__sys_sigwait(thr_remove_thr_signals(set, &newset), sig)); 72405c3a5eaSDavid Xu } 72505c3a5eaSDavid Xu 726635f917aSDavid Xu /* 727635f917aSDavid Xu * Cancellation behavior: 728635f917aSDavid Xu * Thread may be canceled at start, if thread got signal, 729635f917aSDavid Xu * it is not canceled. 730635f917aSDavid Xu */ 73105c3a5eaSDavid Xu int 732*8495e8b1SKonstantin Belousov __thr_sigwait(const sigset_t *set, int *sig) 733a091d823SDavid Xu { 734a091d823SDavid Xu struct pthread *curthread = _get_curthread(); 735a091d823SDavid Xu sigset_t newset; 736a091d823SDavid Xu int ret; 737a091d823SDavid Xu 73883c9e089SDavid Xu do { 73902c3c858SDavid Xu _thr_cancel_enter(curthread); 74082746ea5SDavid Xu ret = __sys_sigwait(thr_remove_thr_signals(set, &newset), sig); 74102c3c858SDavid Xu _thr_cancel_leave(curthread, (ret != 0)); 74283c9e089SDavid Xu } while (ret == EINTR); 743a091d823SDavid Xu return (ret); 744bb535300SJeff Roberson } 7455cf22195SDavid Xu 7465cf22195SDavid Xu int 747*8495e8b1SKonstantin Belousov __thr_setcontext(const ucontext_t *ucp) 7485cf22195SDavid Xu { 7495cf22195SDavid Xu ucontext_t uc; 7505cf22195SDavid Xu 75166f6c272SDavid Xu if (ucp == NULL) { 75266f6c272SDavid Xu errno = EINVAL; 75366f6c272SDavid Xu return (-1); 75466f6c272SDavid Xu } 7558bbeb7e9SDavid Xu if (!SIGISMEMBER(uc.uc_sigmask, SIGCANCEL)) 7568bbeb7e9SDavid Xu return __sys_setcontext(ucp); 7575cf22195SDavid Xu (void) memcpy(&uc, ucp, sizeof(uc)); 7588bbeb7e9SDavid Xu SIGDELSET(uc.uc_sigmask, SIGCANCEL); 759*8495e8b1SKonstantin Belousov return (__sys_setcontext(&uc)); 7605cf22195SDavid Xu } 7615cf22195SDavid Xu 762acad2b1eSDavid Xu int 763*8495e8b1SKonstantin Belousov __thr_swapcontext(ucontext_t *oucp, const ucontext_t *ucp) 764acad2b1eSDavid Xu { 765acad2b1eSDavid Xu ucontext_t uc; 766acad2b1eSDavid Xu 76766f6c272SDavid Xu if (oucp == NULL || ucp == NULL) { 76866f6c272SDavid Xu errno = EINVAL; 76966f6c272SDavid Xu return (-1); 77066f6c272SDavid Xu } 7718bbeb7e9SDavid Xu if (SIGISMEMBER(ucp->uc_sigmask, SIGCANCEL)) { 772acad2b1eSDavid Xu (void) memcpy(&uc, ucp, sizeof(uc)); 7738bbeb7e9SDavid Xu SIGDELSET(uc.uc_sigmask, SIGCANCEL); 7748bbeb7e9SDavid Xu ucp = &uc; 7758bbeb7e9SDavid Xu } 776*8495e8b1SKonstantin Belousov return (__sys_swapcontext(oucp, ucp)); 777acad2b1eSDavid Xu } 778