summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authordavidxu <davidxu@FreeBSD.org>2003-07-17 22:52:55 +0000
committerdavidxu <davidxu@FreeBSD.org>2003-07-17 22:52:55 +0000
commit97d2d9dfed7fc1f9fa26e38c42c4de810d167012 (patch)
tree5e3b89a9cdd74d4cc57a7d15afad9a3373552ecb /sys
parentfd1c90fb6f321674f766c628fd63fa44b304fc91 (diff)
downloadFreeBSD-src-97d2d9dfed7fc1f9fa26e38c42c4de810d167012.zip
FreeBSD-src-97d2d9dfed7fc1f9fa26e38c42c4de810d167012.tar.gz
Fix sigwait to conform to POSIX.
When a signal is being delivered to process, first find a sigwait thread to deliver, POSIX's argument is speed of delivering signal to sigwait thread is faster than other ways. A signal in its wait set will cause sigwait to return the signal number, a signal not in its wait set but in not blocked by the thread also causes sigwait to return, but sigwait returns EINTR, sigwait is oneshot operation, only one signal can be delivered to its wait set, when a signal is delivered to the sigwait thread, the thread's sigwait state is canceled.
Diffstat (limited to 'sys')
-rw-r--r--sys/kern/kern_sig.c176
1 files changed, 109 insertions, 67 deletions
diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c
index 07a4efb..d6e2fc7 100644
--- a/sys/kern/kern_sig.c
+++ b/sys/kern/kern_sig.c
@@ -840,34 +840,70 @@ sigwaitinfo(struct thread *td, struct sigwaitinfo_args *uap)
}
static int
-kern_sigtimedwait(struct thread *td, sigset_t set, siginfo_t *info,
+kern_sigtimedwait(struct thread *td, sigset_t waitset, siginfo_t *info,
struct timespec *timeout)
{
- register struct sigacts *ps;
- sigset_t oldmask;
- struct proc *p;
+ struct sigacts *ps;
+ sigset_t savedmask, sigset;
+ struct proc *p;
int error;
int sig;
int hz;
+ int i;
p = td->td_proc;
error = 0;
sig = 0;
- SIG_CANTMASK(set);
+ SIG_CANTMASK(waitset);
PROC_LOCK(p);
ps = p->p_sigacts;
- oldmask = td->td_sigmask;
- SIGFILLSET(td->td_sigmask);
- SIG_CANTMASK(td->td_sigmask);
- SIGSETNAND(td->td_sigmask, set);
- signotify(td);
+ savedmask = td->td_sigmask;
- mtx_lock(&ps->ps_mtx);
- sig = cursig(td);
- if (sig)
+again:
+ for (i = 1; i <= _SIG_MAXSIG; ++i) {
+ if (!SIGISMEMBER(waitset, i))
+ continue;
+ if (SIGISMEMBER(td->td_siglist, i)) {
+ SIGFILLSET(td->td_sigmask);
+ SIG_CANTMASK(td->td_sigmask);
+ SIGDELSET(td->td_sigmask, i);
+ mtx_lock(&ps->ps_mtx);
+ sig = cursig(td);
+ i = 0;
+ mtx_unlock(&ps->ps_mtx);
+ } else if (SIGISMEMBER(p->p_siglist, i)) {
+ if (p->p_flag & P_SA) {
+ p->p_flag |= P_SIGEVENT;
+ wakeup(&p->p_siglist);
+ }
+ SIGDELSET(p->p_siglist, i);
+ SIGADDSET(td->td_siglist, i);
+ SIGFILLSET(td->td_sigmask);
+ SIG_CANTMASK(td->td_sigmask);
+ SIGDELSET(td->td_sigmask, i);
+ mtx_lock(&ps->ps_mtx);
+ sig = cursig(td);
+ i = 0;
+ mtx_unlock(&ps->ps_mtx);
+ }
+ if (sig) {
+ td->td_sigmask = savedmask;
+ signotify(td);
+ goto out;
+ }
+ }
+ if (error)
goto out;
+ td->td_sigmask = savedmask;
+ signotify(td);
+ sigset = td->td_siglist;
+ SIGSETOR(sigset, p->p_siglist);
+ SIGSETAND(sigset, waitset);
+ if (!SIGISEMPTY(sigset))
+ goto again;
+
/*
* POSIX says this must be checked after looking for pending
* signals.
@@ -879,46 +915,40 @@ kern_sigtimedwait(struct thread *td, sigset_t set, siginfo_t *info,
error = EINVAL;
goto out;
}
- if (timeout->tv_sec == 0 && timeout->tv_nsec == 0)
- goto nosleep;
+ if (timeout->tv_sec == 0 && timeout->tv_nsec == 0) {
+ error = EAGAIN;
+ goto out;
+ }
TIMESPEC_TO_TIMEVAL(&tv, timeout);
hz = tvtohz(&tv);
} else
hz = 0;
- mtx_unlock(&ps->ps_mtx);
- error = msleep(ps, &p->p_mtx, PPAUSE|PCATCH, "pause", hz);
- mtx_lock(&ps->ps_mtx);
- if (error == EINTR)
- error = 0;
- else if (error)
- goto out;
-nosleep:
- sig = cursig(td);
+ td->td_waitset = &waitset;
+ error = msleep(ps, &p->p_mtx, PPAUSE|PCATCH, "sigwait", hz);
+ td->td_waitset = NULL;
+ if (error == 0) /* surplus wakeup ? */
+ error = EINTR;
+ goto again;
out:
- td->td_sigmask = oldmask;
if (sig) {
sig_t action;
+ error = 0;
+ mtx_lock(&ps->ps_mtx);
action = ps->ps_sigact[_SIG_IDX(sig)];
mtx_unlock(&ps->ps_mtx);
#ifdef KTRACE
if (KTRPOINT(td, KTR_PSIG))
- ktrpsig(sig, action, td->td_pflags & TDP_OLDMASK ?
- &td->td_oldsigmask : &td->td_sigmask, 0);
+ ktrpsig(sig, action, &td->td_sigmask, 0);
#endif
_STOPEVENT(p, S_SIG, sig);
- if (action == SIG_DFL)
- sigexit(td, sig);
- /* NOTREACHED */
-
SIGDELSET(td->td_siglist, sig);
info->si_signo = sig;
info->si_code = 0;
- } else
- mtx_unlock(&ps->ps_mtx);
+ }
PROC_UNLOCK(p);
return (error);
}
@@ -1478,11 +1508,6 @@ trapsignal(struct thread *td, int sig, u_long code)
SIGDELSET(td->td_sigmask, sig);
mtx_lock_spin(&sched_lock);
/*
- * don't psignal to the thread, it only can accept
- * its own sync signal.
- */
- td->td_flags |= TDF_NOSIGPOST;
- /*
* Force scheduling an upcall, so UTS has chance to
* process the signal before thread runs again in
* userland.
@@ -1546,37 +1571,35 @@ trapsignal(struct thread *td, int sig, u_long code)
static struct thread *
sigtd(struct proc *p, int sig, int prop)
{
- struct thread *td;
+ struct thread *td, *signal_td;
PROC_LOCK_ASSERT(p, MA_OWNED);
/*
- * If we know the signal is bound for a specific thread then we
- * assume that we are in that threads context. This is the case
- * for SIGXCPU, SIGILL, etc. Otherwise someone did a kill() from
- * userland, if current thread can handle the signal, it should
- * get the signal.
- */
- if (curthread->td_proc == p && !SIGISMEMBER(curthread->td_sigmask, sig))
- return (curthread);
-
- /*
- * We should search for the first thread that is blocked in
- * sigsuspend with this signal unmasked.
+ * First find a thread in sigwait state and signal belongs to
+ * its wait set. POSIX's arguments is that speed of delivering signal
+ * to sigwait thread is faster than delivering signal to user stack.
+ * If we can not find sigwait thread, then find the first thread in
+ * the proc that doesn't have this signal masked, an exception is
+ * if current thread is sending signal to its process, and it does not
+ * mask the signal, it should get the signal, this is another fast
+ * way to deliver signal.
*/
-
- /* XXX */
-
- /*
- * Find the first thread in the proc that doesn't have this signal
- * masked.
- */
- FOREACH_THREAD_IN_PROC(p, td)
- if (!SIGISMEMBER(td->td_sigmask, sig) &&
- !(td->td_flags & TDF_NOSIGPOST))
- return (td);
-
- return (FIRST_THREAD_IN_PROC(p));
+ signal_td = NULL;
+ FOREACH_THREAD_IN_PROC(p, td) {
+ if (td->td_waitset != NULL &&
+ SIGISMEMBER(*(td->td_waitset), sig))
+ return (td);
+ if (!SIGISMEMBER(td->td_sigmask, sig)) {
+ if (td == curthread)
+ signal_td = curthread;
+ else if (signal_td == NULL)
+ signal_td = td;
+ }
+ }
+ if (signal_td == NULL)
+ signal_td = FIRST_THREAD_IN_PROC(p);
+ return (signal_td);
}
/*
@@ -1660,8 +1683,17 @@ do_tdsignal(struct thread *td, int sig, sigtarget_t target)
* assign it to the process so that we can find it later in the first
* thread that unblocks it. Otherwise, assign it to this thread now.
*/
- siglist = (target != SIGTARGET_TD && SIGISMEMBER(td->td_sigmask, sig)) ?
- &p->p_siglist : &td->td_siglist;
+ if (target == SIGTARGET_TD) {
+ siglist = &td->td_siglist;
+ } else {
+ if (!SIGISMEMBER(td->td_sigmask, sig))
+ siglist = &td->td_siglist;
+ else if (td->td_waitset != NULL &&
+ SIGISMEMBER(*(td->td_waitset), sig))
+ siglist = &td->td_siglist;
+ else
+ siglist = &p->p_siglist;
+ }
/*
* If proc is traced, always give parent a chance;
@@ -1684,7 +1716,11 @@ do_tdsignal(struct thread *td, int sig, sigtarget_t target)
mtx_unlock(&ps->ps_mtx);
return;
}
- if (SIGISMEMBER(td->td_sigmask, sig))
+ if (((td->td_waitset == NULL) &&
+ SIGISMEMBER(td->td_sigmask, sig)) ||
+ ((td->td_waitset != NULL) &&
+ SIGISMEMBER(td->td_sigmask, sig) &&
+ !SIGISMEMBER(*(td->td_waitset), sig)))
action = SIG_HOLD;
else if (SIGISMEMBER(ps->ps_sigcatch, sig))
action = SIG_CATCH;
@@ -1719,8 +1755,14 @@ do_tdsignal(struct thread *td, int sig, sigtarget_t target)
SIG_CONTSIGMASK(td0->td_siglist);
p->p_flag &= ~P_CONTINUED;
}
+
SIGADDSET(*siglist, sig);
signotify(td); /* uses schedlock */
+ if (siglist == &td->td_siglist && (td->td_waitset != NULL) &&
+ action != SIG_HOLD) {
+ td->td_waitset = NULL;
+ }
+
/*
* Defer further processing for signals which are held,
* except that stopped processes must be continued by SIGCONT.
OpenPOWER on IntegriCloud