diff options
Diffstat (limited to 'lib/libpthread/thread/thr_sigwait.c')
-rw-r--r-- | lib/libpthread/thread/thr_sigwait.c | 130 |
1 files changed, 127 insertions, 3 deletions
diff --git a/lib/libpthread/thread/thr_sigwait.c b/lib/libpthread/thread/thr_sigwait.c index 5364d66..b955251 100644 --- a/lib/libpthread/thread/thr_sigwait.c +++ b/lib/libpthread/thread/thr_sigwait.c @@ -42,11 +42,135 @@ __weak_reference(_sigwait, sigwait); int -_sigwait(const sigset_t * __restrict set, int * __restrict sig) +_sigwait(const sigset_t *set, int *sig) { + struct pthread *curthread = _get_curthread(); + int ret = 0; + int i; + sigset_t tempset, waitset; + struct sigaction act; + + _thr_enter_cancellation_point(curthread); /* - * All signals are invalid for waiting. + * Specify the thread kernel signal handler. */ - return (EINVAL); + act.sa_handler = (void (*) ()) _thr_sig_handler; + act.sa_flags = SA_RESTART | SA_SIGINFO; + /* Ensure the signal handler cannot be interrupted by other signals: */ + sigfillset(&act.sa_mask); + + /* + * Initialize the set of signals that will be waited on: + */ + waitset = *set; + + /* These signals can't be waited on. */ + sigdelset(&waitset, SIGKILL); + sigdelset(&waitset, SIGSTOP); + + /* + * Check to see if a pending signal is in the wait mask. + * This has to be atomic. */ + tempset = curthread->sigpend; + SIGSETOR(tempset, _thr_proc_sigpending); + SIGSETAND(tempset, waitset); + if (SIGNOTEMPTY(tempset)) { + /* Enter a loop to find a pending signal: */ + for (i = 1; i < NSIG; i++) { + if (sigismember (&tempset, i)) + break; + } + + /* Clear the pending signal: */ + if (sigismember(&curthread->sigpend,i)) + sigdelset(&curthread->sigpend,i); + else + sigdelset(&_thr_proc_sigpending,i); + + /* Return the signal number to the caller: */ + *sig = i; + + _thr_leave_cancellation_point(curthread); + return (0); + } + + /* + * Lock the array of SIG_DFL wait counts. + */ + THR_LOCK_ACQUIRE(curthread, &_thread_signal_lock); + + /* + * Enter a loop to find the signals that are SIG_DFL. For + * these signals we must install a dummy signal handler in + * order for the kernel to pass them in to us. POSIX says + * that the _application_ must explicitly install a dummy + * handler for signals that are SIG_IGN in order to sigwait + * on them. Note that SIG_IGN signals are left in the + * mask because a subsequent sigaction could enable an + * ignored signal. + */ + sigemptyset(&tempset); + for (i = 1; i < NSIG; i++) { + if (sigismember(&waitset, i) && + (_thread_sigact[i - 1].sa_handler == SIG_DFL)) { + _thread_dfl_count[i]++; + sigaddset(&tempset, i); + if (_thread_dfl_count[i] == 1) { + if (__sys_sigaction(i, &act, NULL) != 0) + ret = -1; + } + } + } + /* Done accessing _thread_dfl_count for now. */ + THR_LOCK_RELEASE(curthread, &_thread_signal_lock); + + if (ret == 0) { + /* + * Save the wait signal mask. The wait signal + * mask is independent of the threads signal mask + * and requires separate storage. + */ + curthread->data.sigwait = &waitset; + + /* Wait for a signal: */ + THR_SCHED_LOCK(curthread, curthread); + THR_SET_STATE(curthread, PS_SIGWAIT); + THR_SCHED_UNLOCK(curthread, curthread); + _thr_sched_switch(curthread); + + /* Return the signal number to the caller: */ + *sig = curthread->signo; + + /* + * Probably unnecessary, but since it's in a union struct + * we don't know how it could be used in the future. + */ + curthread->data.sigwait = NULL; + } + + /* + * Relock the array of SIG_DFL wait counts. + */ + THR_LOCK_ACQUIRE(curthread, &_thread_signal_lock); + + /* Restore the sigactions: */ + act.sa_handler = SIG_DFL; + for (i = 1; i < NSIG; i++) { + if (sigismember(&tempset, i)) { + _thread_dfl_count[i]--; + if ((_thread_sigact[i - 1].sa_handler == SIG_DFL) && + (_thread_dfl_count[i] == 0)) { + if (__sys_sigaction(i, &act, NULL) != 0) + ret = -1; + } + } + } + /* Done accessing _thread_dfl_count. */ + THR_LOCK_RELEASE(curthread, &_thread_signal_lock); + + _thr_leave_cancellation_point(curthread); + + /* Return the completion status: */ + return (ret); } |