Files
Arquivotheca.Solaris-2.5/lib/libthread/sys/common/sigwait.c
seta75D 7c4988eac0 Init
2021-10-11 19:38:01 -03:00

320 lines
8.2 KiB
C
Executable File

/* 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 <signal.h>
#include <errno.h>
/*
* 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);
}
}