/* Copyright (c) 1993 SMI */ /* All Rights Reserved */ /* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF SMI */ /* The copyright notice above does not evidence any */ /* actual or intended publication of such source code. */ #pragma ident "@(#)sigwait.c 1.20 95/08/08 SMI" #ifdef __STDC__ #pragma weak sigwait = _sigwait #endif /* __STDC__ */ #include "libthread.h" #include #include /* * GLobal variables */ cond_t _sigwait_cv = DEFAULTCV; /* * Static functions */ static int get_sig(sigset_t *smask, const sigset_t *rmask, int *sigp, siginfo_t *sip); static int cond_wait_sig(cond_t *cvp, sigset_t *set); static void dummy_hdlr(); #define maskreverse(s1, s2) {\ int i; \ for (i = 0; i < 4; i++)\ (s2)->__sigbits[i] = ~(s1)->__sigbits[i]; \ } #define pending(s1, s2, s3)(\ ((s3)->__sigbits[0] = (s1)->__sigbits[0] & (s2)->__sigbits[0]) ||\ ((s3)->__sigbits[1] = (s1)->__sigbits[1] & (s2)->__sigbits[1]) ||\ ((s3)->__sigbits[2] = (s1)->__sigbits[2] & (s2)->__sigbits[2]) ||\ ((s3)->__sigbits[3] = (s1)->__sigbits[3] & (s2)->__sigbits[3])) #define TDIR 1 #define PDIR 2 static struct timespec __zero_to = {0, 0}; /* * Return 1 if the signal was directed to this thread via thr_kill(). * Return 2 if the signal was directed to the process. * Return 0 if no signal was found. * Return EINTR if signal not in signal set being waited for was found. * Return -1 on error. */ static int get_sig(sigset_t *smask, const sigset_t *rmask, int *sigp, siginfo_t *sip) { uthread_t *t = curthread; int sig = 0; int ret = 0; sigset_t found; sigset_t sigwsig; sigset_t sigs; ASSERT(MUTEX_HELD(&_schedlock)); ASSERT(MUTEX_HELD(&_pmasklock)); ASSERT(sigp != NULL); *sigp = 0; sigemptyset(&found); maskreverse(smask, &sigs); if (pending(&t->t_psig, &sigs, &found)) { sig = _fsig(&found); ASSERT(sig != 0); if (sigismember(rmask, sig)) { sigdelset(&t->t_psig, sig); sigdelset(&t->t_ssig, sig); t->t_pending = !sigisempty(&t->t_psig); *sigp = sig; if (sip != NULL) { sip->si_signo = sig; sip->si_code = SI_NOINFO; } ret = TDIR; } else ret = EINTR; } else if (pending(&_pmask, &sigs, &found)) { sig = _fsig(&found); ASSERT(sig != 0); if (sigismember(rmask, sig)) { sigdelset(&_pmask, sig); sigemptyset(&sigwsig); sigaddset(&sigwsig, sig); if ((*__sigtimedwait_trap)(&sigwsig, sip, &__zero_to) == -1) { /* * The zero timeout above ensures a non-blocking * call. The signal may not be found in the * kernel - this is possible only if the current * process is the child of a MT process which * had pending signals at the time fork() or * fork1() was called. The kernel clears all * pending signals in the child of the fork. * However, the user-level mirror of pending * signals is not cleared at the time of the * fork. It is simple for the kernel to clear * all pending signals, since they are all at * one place (p_notifsigs), but it is harder for * the user level since the mirror is a union * of _pmask and all threads' t_bsig masks. * For the child of a fork1(), clearing the user * mirror is much easier (just clear _pmask in * _resetlib()), but for the child of a fork(), * with cloned threads, this is much harder. * The solution adopted here is a general * solution for both fork() and fork1() - but * more for the fork() case. The solution is to * clear the user-level mirror lazily, as and * when required. If errno == EAGAIN, this * process must be the child of a fork in this * situation. If so, do not set "ret" to -1, * return zero in "ret" and *sigp, which causes * the thread to go back to sleep in sigwait(). * This is like a spurious wake-up which lazily * clears or corrects the pending signal state * at user-level. * Same comment applies in the t_bsig case below */ if (errno != EAGAIN) ret = -1; } else { *sigp = sig; ret = PDIR; } } else ret = EINTR; } else if (pending(&t->t_bsig, &sigs, &found)) { sig = _fsig(&found); ASSERT(sig != 0); if (sigismember(rmask, sig)) { sigdelset(&t->t_bsig, sig); t->t_bdirpend = !sigisempty(&t->t_bsig); sigemptyset(&sigwsig); sigaddset(&sigwsig, sig); if ((*__sigtimedwait_trap)(&sigwsig, sip, &__zero_to) == -1) { if (errno == EAGAIN) /* * See big comment above about lazy * clearing of pending signals. */ ret = -1; } else { *sigp = sig; ret = PDIR; } } else ret = EINTR; } return (ret); } /* * Atomically wait, release lock and unblock signals in "set". * XXX: Reduce the number of arguments! */ static int cond_timedwait_sig(cond_t *cvp, sigset_t *smask, const sigset_t *rmask, int *psigp, siginfo_t *info, int *dir, const struct timespec *to) { uthread_t *t = curthread; struct timeval tv; int cv_timedout = 0; int ret; extern void _setrun(); ASSERT(MUTEX_HELD(&_schedlock)); ASSERT(MUTEX_HELD(&_pmasklock)); ASSERT(psigp != NULL); ASSERT(*psigp == 0); ASSERT(dir != NULL); if (to != NULL) { _lwp_mutex_unlock(&_pmasklock); _sched_unlock_nosig(); tv.tv_sec = to->tv_sec; tv.tv_usec = to->tv_nsec/1000; _setcallout(&curthread->t_cv_callo, NULL, &tv, _setrun, (int)curthread); _sched_lock(); _lwp_mutex_lock(&_pmasklock); ret = get_sig(smask, rmask, psigp, info); *dir = ret; if (*psigp != 0 || ret == -1 || ret == EINTR) { /* * got a signal while establishing timeout. */ _rmcallout(&curthread->t_cv_callo); return (ret); } else if (ISTIMEDOUT(&curthread->t_cv_callo)) { return (ETIME); } } t->t_flag &= ~T_INTR; t->t_flag |= T_WAITCV; _t_block((caddr_t)cvp); cvp->cond_waiters = 1; _lwp_mutex_unlock(&_pmasklock); _sched_unlock_nosig(); _thr_sigsetmask(SIG_SETMASK, smask, NULL); _cancelon(); _swtch(0); _canceloff(); if (to != NULL) cv_timedout = _rmcallout(&curthread->t_cv_callo); maskallsigs(&t->t_hold); _sched_lock_nosig(); t->t_flag &= ~T_INTR; _lwp_mutex_lock(&_pmasklock); ret = get_sig(smask, rmask, psigp, info); *dir = ret; if (cv_timedout == ETIME) return (ETIME); else return (ret); } int _sigwait(const sigset_t *set) { return (__sigtimedwait(set, NULL, NULL)); } #define NANOSEC 1000000000 /* * This routine __sigtimedwait() interposes on __sigtimedwait() exported by * libc for libposix4. So the name should not be changed without also changing * libc and libposix4. */ int __sigtimedwait(const sigset_t *set, siginfo_t *info, const struct timespec *to) { uthread_t *t = curthread; sigset_t othmask; sigset_t sleepmask, newset; int sig = 0; int zerotimeout = 0; int breakloop = 0; int dir = 0; int ret = 0; if (to != NULL) { if (to->tv_sec < 0 || to->tv_nsec < 0 || to->tv_nsec >= NANOSEC) { errno = EINVAL; return (-1); } if (to->tv_sec == 0 && to->tv_nsec == 0) zerotimeout = 1; } t->t_flag |= T_SIGWAIT; /* * The following is to make sure there is nothing pending on the * underlying LWP. Masking all signals ensures this. */ _thr_sigsetmask(SIG_SETMASK, &_allmasked, &othmask); sleepmask = othmask; sigdiffset(&sleepmask, set); maskreverse(&sleepmask, &newset); _sched_lock(); _lwp_mutex_lock(&_pmasklock); ret = get_sig(&sleepmask, set, &sig, info); if (sig == 0 && zerotimeout == 1) { _lwp_mutex_unlock(&_pmasklock); _sched_unlock(); errno = EAGAIN; _thr_sigsetmask(SIG_SETMASK, &othmask, NULL); return (-1); } while (sig == 0 && breakloop == 0) { if ((ret = cond_timedwait_sig(&_sigwait_cv, &sleepmask, set, &sig, info, &dir, to)) == ETIME || ret == EINTR) breakloop = 1; else if (ret == -1) { _lwp_mutex_unlock(&_pmasklock); _sched_unlock_nosig(); _thr_sigsetmask(SIG_SETMASK, &othmask, NULL); _sigon(); return (-1); } } if (sig != 0) { ASSERT(sigismember(set, sig)); _lwp_mutex_unlock(&_pmasklock); t->t_flag &= ~T_SIGWAIT; _sched_unlock_nosig(); _thr_sigsetmask(SIG_SETMASK, &othmask, NULL); _sigon(); return (sig); } else { ASSERT(breakloop != 0); _lwp_mutex_unlock(&_pmasklock); t->t_flag &= ~T_SIGWAIT; _sched_unlock_nosig(); _thr_sigsetmask(SIG_SETMASK, &othmask, NULL); _sigon(); if (ret == ETIME) errno = EAGAIN; else errno = ret; return (-1); } }