diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c index e174df1..2ad2bab 100644 --- a/sys/kern/kern_sig.c +++ b/sys/kern/kern_sig.c @@ -1450,8 +1450,13 @@ sigsuspend(td, uap) int kern_sigsuspend(struct thread *td, sigset_t mask) { - struct proc *p = td->td_proc; - int has_sig, sig; + struct proc *p; + struct sigacts *ps; + sigset_t savedmask; + int i, sig; + + p = td->td_proc; + ps = p->p_sigacts; /* * When returning from sigsuspend, we want @@ -1466,23 +1471,45 @@ kern_sigsuspend(struct thread *td, sigset_t mask) td->td_pflags |= TDP_OLDMASK; /* - * Process signals now. Otherwise, we can get spurious wakeup - * due to signal entered process queue, but delivered to other - * thread. But sigsuspend should return only on signal - * delivery. + * Make sure that td has a signal to deliver. Otherwise, we + * can get spurious wakeup due to signal entered process + * queue, but delivered to other thread. But sigsuspend should + * return only on signal delivery. */ - for (has_sig = 0; !has_sig;) { + for (;;) { + restart: + for (i = 1; i <= _SIG_MAXSIG; i++) { + if (SIGISMEMBER(td->td_sigmask, i)) + continue; + if (!SIGISMEMBER(td->td_sigqueue.sq_signals, i)) { + if (SIGISMEMBER(p->p_sigqueue.sq_signals, i)) { + sigqueue_move(&p->p_sigqueue, + &td->td_sigqueue, i); + } else + continue; + } + savedmask = td->td_sigmask; + SIGFILLSET(td->td_sigmask); + SIG_CANTMASK(td->td_sigmask); + SIGDELSET(td->td_sigmask, i); + mtx_lock(&ps->ps_mtx); + sig = cursig(td, SIG_STOP_ALLOWED); + mtx_unlock(&ps->ps_mtx); + td->td_sigmask = savedmask; + if (sig) + goto out; + else + goto restart; + } + while (msleep(&p->p_sigacts, &p->p_mtx, PPAUSE|PCATCH, "pause", - 0) == 0) + 0) == 0) /* void */; thread_suspend_check(0); - mtx_lock(&p->p_sigacts->ps_mtx); - while ((sig = cursig(td, SIG_STOP_ALLOWED)) != 0) { - postsig(sig); - has_sig = 1; - } - mtx_unlock(&p->p_sigacts->ps_mtx); } + + out: + signotify(td); PROC_UNLOCK(p); /* always return EINTR rather than ERESTART... */ return (EINTR);