On Thu, May 09, 2019 at 11:01:13PM -0400, Alexander Bluhm wrote:
> When killing a process, the signal is handled by any thread that
> does not block the signal.  If all threads block the signal, we
> currently deliver it to the main thread.  This does not conform to
> POSIX.  If any thread unblocks the signal, it should be delivered
> immediately to this thread.  We block it until the main thread
> unblocks.
>
> I see the problem as some tests of posixtestsuite hang.  Various
> people reported similar observations with multi threaded programs
> to me.
>
> Solution is to mark such signals at the process instead of one
> thread.  Then any thread can handle it later.  There is a XXX comment
> that suggests this behavior, NetBSD also does this.
>
> This diff survived a full regress run on i386.

I have written a regress test that has showed a bug I introdced.
In exit1() I have to clear pr->ps_siglist only when exiting the
main thread.

new diff, ok?

bluhm

Index: kern/exec_elf.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/kern/exec_elf.c,v
retrieving revision 1.149
diff -u -p -r1.149 exec_elf.c
--- kern/exec_elf.c     9 May 2019 22:25:42 -0000       1.149
+++ kern/exec_elf.c     10 May 2019 02:39:07 -0000
@@ -1224,7 +1224,7 @@ coredump_notes_elf(struct proc *p, void
                cpi.cpi_signo = p->p_sisig;
                cpi.cpi_sigcode = p->p_sicode;

-               cpi.cpi_sigpend = p->p_siglist;
+               cpi.cpi_sigpend = p->p_siglist | pr->ps_siglist;
                cpi.cpi_sigmask = p->p_sigmask;
                cpi.cpi_sigignore = pr->ps_sigacts->ps_sigignore;
                cpi.cpi_sigcatch = pr->ps_sigacts->ps_sigcatch;
Index: kern/kern_exit.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/kern/kern_exit.c,v
retrieving revision 1.173
diff -u -p -r1.173 kern_exit.c
--- kern/kern_exit.c    23 Jan 2019 22:39:47 -0000      1.173
+++ kern/kern_exit.c    11 May 2019 04:48:49 -0000
@@ -180,6 +180,8 @@ exit1(struct proc *p, int rv, int flags)
                }
        }
        p->p_siglist = 0;
+       if ((p->p_flag & P_THREAD) == 0)
+               pr->ps_siglist = 0;

 #if NKCOV > 0
        kcov_exit(p);
Index: kern/kern_sig.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/kern/kern_sig.c,v
retrieving revision 1.229
diff -u -p -r1.229 kern_sig.c
--- kern/kern_sig.c     1 May 2019 06:26:42 -0000       1.229
+++ kern/kern_sig.c     11 May 2019 04:17:30 -0000
@@ -365,6 +365,7 @@ setsigvec(struct proc *p, int signum, st
        if (sa->sa_handler == SIG_IGN ||
            (sigprop[signum] & SA_IGNORE && sa->sa_handler == SIG_DFL)) {
                atomic_clearbits_int(&p->p_siglist, bit);
+               atomic_clearbits_int(&p->p_p->ps_siglist, bit);
                if (signum != SIGCONT)
                        ps->ps_sigignore |= bit;        /* easier in psignal */
                ps->ps_sigcatch &= ~bit;
@@ -419,6 +420,7 @@ execsigs(struct proc *p)
                        if (nc != SIGCONT)
                                ps->ps_sigignore |= mask;
                        atomic_clearbits_int(&p->p_siglist, mask);
+                       atomic_clearbits_int(&p->p_p->ps_siglist, mask);
                }
                ps->ps_sigact[nc] = SIG_DFL;
        }
@@ -472,7 +474,7 @@ int
 sys_sigpending(struct proc *p, void *v, register_t *retval)
 {

-       *retval = p->p_siglist;
+       *retval = p->p_siglist | p->p_p->ps_siglist;
        return (0);
 }

@@ -875,7 +877,6 @@ psignal(struct proc *p, int signum)

 /*
  * type = SPROCESS     process signal, can be diverted (sigwait())
- *     XXX if blocked in all threads, mark as pending in struct process
  * type = STHREAD      thread signal, but should be propagated if unhandled
  * type = SPROPAGATED  propagated to this thread, so don't propagate again
  */
@@ -885,6 +886,7 @@ ptsignal(struct proc *p, int signum, enu
        int s, prop;
        sig_t action;
        int mask;
+       int *siglist;
        struct process *pr = p->p_p;
        struct proc *q;
        int wakeparent = 0;
@@ -907,7 +909,7 @@ ptsignal(struct proc *p, int signum, enu
                if (pr->ps_flags & PS_COREDUMP && signum == SIGKILL) {
                        if (pr->ps_single != NULL)
                                p = pr->ps_single;
-                       atomic_setbits_int(&p->p_siglist, mask);
+                       atomic_setbits_int(&p->p_p->ps_siglist, mask);
                        return;
                }

@@ -962,7 +964,6 @@ ptsignal(struct proc *p, int signum, enu
         */
        if (pr->ps_flags & PS_TRACED) {
                action = SIG_DFL;
-               atomic_setbits_int(&p->p_siglist, mask);
        } else {
                /*
                 * If the signal is being ignored,
@@ -992,17 +993,23 @@ ptsignal(struct proc *p, int signum, enu
                        if (prop & SA_TTYSTOP && pr->ps_pgrp->pg_jobc == 0)
                                return;
                }
-
-               atomic_setbits_int(&p->p_siglist, mask);
        }
-
-       if (prop & SA_CONT)
-               atomic_clearbits_int(&p->p_siglist, stopsigmask);
-
+       /*
+        * If delivered to process, mark as pending there.  Continue and stop
+        * signals will be propagated to all threads.  So they are always
+        * marked at thread level.
+        */
+       siglist = (type == SPROCESS) ? &pr->ps_siglist : &p->p_siglist;
+       if (prop & SA_CONT) {
+               siglist = &p->p_siglist;
+               atomic_clearbits_int(siglist, stopsigmask);
+       }
        if (prop & SA_STOP) {
-               atomic_clearbits_int(&p->p_siglist, contsigmask);
+               siglist = &p->p_siglist;
+               atomic_clearbits_int(siglist, contsigmask);
                atomic_clearbits_int(&p->p_flag, P_CONTINUED);
        }
+       atomic_setbits_int(siglist, mask);

        /*
         * XXX delay processing of SA_STOP signals unless action == SIG_DFL?
@@ -1045,7 +1052,7 @@ ptsignal(struct proc *p, int signum, enu
                 * be awakened.
                 */
                if ((prop & SA_CONT) && action == SIG_DFL) {
-                       atomic_clearbits_int(&p->p_siglist, mask);
+                       atomic_clearbits_int(siglist, mask);
                        goto out;
                }
                /*
@@ -1059,7 +1066,7 @@ ptsignal(struct proc *p, int signum, enu
                         */
                        if (pr->ps_flags & PS_PPWAIT)
                                goto out;
-                       atomic_clearbits_int(&p->p_siglist, mask);
+                       atomic_clearbits_int(siglist, mask);
                        p->p_xstat = signum;
                        proc_stop(p, 0);
                        goto out;
@@ -1102,7 +1109,7 @@ ptsignal(struct proc *p, int signum, enu
                        atomic_clearbits_int(&p->p_flag, P_SUSPSIG);
                        wakeparent = 1;
                        if (action == SIG_DFL)
-                               atomic_clearbits_int(&p->p_siglist, mask);
+                               atomic_clearbits_int(siglist, mask);
                        if (action == SIG_CATCH)
                                goto runfast;
                        if (p->p_wchan == 0)
@@ -1116,7 +1123,7 @@ ptsignal(struct proc *p, int signum, enu
                         * Already stopped, don't need to stop again.
                         * (If we did the shell could get confused.)
                         */
-                       atomic_clearbits_int(&p->p_siglist, mask);
+                       atomic_clearbits_int(siglist, mask);
                        goto out;
                }

@@ -1181,7 +1188,7 @@ issignal(struct proc *p)
        int s;

        for (;;) {
-               mask = p->p_siglist & ~p->p_sigmask;
+               mask = SIGPENDING(p);
                if (pr->ps_flags & PS_PPWAIT)
                        mask &= ~stopsigmask;
                if (mask == 0)          /* no signal to send */
@@ -1189,6 +1196,7 @@ issignal(struct proc *p)
                signum = ffs((long)mask);
                mask = sigmask(signum);
                atomic_clearbits_int(&p->p_siglist, mask);
+               atomic_clearbits_int(&p->p_p->ps_siglist, mask);

                /*
                 * We should see pending but ignored signals
@@ -1243,6 +1251,7 @@ issignal(struct proc *p)

                        /* take the signal! */
                        atomic_clearbits_int(&p->p_siglist, mask);
+                       atomic_clearbits_int(&p->p_p->ps_siglist, mask);
                }

                prop = sigprop[signum];
@@ -1638,7 +1647,8 @@ coredump_write(void *cookie, enum uio_se

        csize = len;
        do {
-               if (io->io_proc->p_siglist & sigmask(SIGKILL))
+               if (sigmask(SIGKILL) &
+                   (io->io_proc->p_siglist | io->io_proc->p_p->ps_siglist))
                        return (EINTR);

                /* Rest of the loop sleeps with lock held, so... */
@@ -1740,8 +1750,8 @@ sys___thrsigdivert(struct proc *p, void
                        if (smask & mask) {
                                if (p->p_siglist & smask)
                                        m = &p->p_siglist;
-                               else if (pr->ps_mainproc->p_siglist & smask)
-                                       m = &pr->ps_mainproc->p_siglist;
+                               else if (pr->ps_siglist & smask)
+                                       m = &pr->ps_siglist;
                                else {
                                        /* signal got eaten by someone else? */
                                        continue;
@@ -1868,7 +1878,7 @@ userret(struct proc *p)
                KERNEL_UNLOCK();
        }

-       if (SIGPENDING(p)) {
+       if (SIGPENDING(p) != 0) {
                KERNEL_LOCK();
                while ((signum = CURSIG(p)) != 0)
                        postsig(p, signum);
Index: kern/tty.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/kern/tty.c,v
retrieving revision 1.143
diff -u -p -r1.143 tty.c
--- kern/tty.c  6 Sep 2018 11:50:54 -0000       1.143
+++ kern/tty.c  10 May 2019 02:39:07 -0000
@@ -1676,11 +1676,11 @@ ttycheckoutq(struct tty *tp, int wait)

        hiwat = tp->t_hiwat;
        s = spltty();
-       oldsig = wait ? curproc->p_siglist : 0;
+       oldsig = wait ? SIGPENDING(curproc) : 0;
        if (tp->t_outq.c_cc > hiwat + TTHIWATMINSPACE)
                while (tp->t_outq.c_cc > hiwat) {
                        ttstart(tp);
-                       if (wait == 0 || curproc->p_siglist != oldsig) {
+                       if (wait == 0 || SIGPENDING(curproc) != oldsig) {
                                splx(s);
                                return (0);
                        }
Index: nfs/nfs_socket.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/nfs/nfs_socket.c,v
retrieving revision 1.132
diff -u -p -r1.132 nfs_socket.c
--- nfs/nfs_socket.c    9 Nov 2018 14:14:32 -0000       1.132
+++ nfs/nfs_socket.c    10 May 2019 02:39:07 -0000
@@ -1225,9 +1225,8 @@ nfs_sigintr(struct nfsmount *nmp, struct
                return (EINTR);
        if (!(nmp->nm_flag & NFSMNT_INT))
                return (0);
-       if (p && p->p_siglist &&
-           (((p->p_siglist & ~p->p_sigmask) &
-           ~p->p_p->ps_sigacts->ps_sigignore) & NFSINT_SIGMASK))
+       if (p && (SIGPENDING(p) & ~p->p_p->ps_sigacts->ps_sigignore &
+           NFSINT_SIGMASK))
                return (EINTR);
        return (0);
 }
Index: sys/proc.h
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/sys/proc.h,v
retrieving revision 1.263
diff -u -p -r1.263 proc.h
--- sys/proc.h  6 Jan 2019 12:59:45 -0000       1.263
+++ sys/proc.h  10 May 2019 02:39:07 -0000
@@ -186,6 +186,7 @@ struct process {
 #define        ps_startzero    ps_klist
        struct  klist ps_klist;         /* knotes attached to this process */
        int     ps_flags;               /* PS_* flags. */
+       int     ps_siglist;             /* Signals pending for the process. */

        struct  proc *ps_single;        /* Single threading to this thread. */
        int     ps_singlecount;         /* Not yet suspended threads. */
Index: sys/signalvar.h
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/sys/signalvar.h,v
retrieving revision 1.35
diff -u -p -r1.35 signalvar.h
--- sys/signalvar.h     17 Dec 2018 14:51:57 -0000      1.35
+++ sys/signalvar.h     10 May 2019 02:39:07 -0000
@@ -67,24 +67,30 @@ struct      sigacts {

 /*
  * Check if process p has an unmasked signal pending.
+ * Return mask of pending signals.
  */
-#define        SIGPENDING(p)   (((p)->p_siglist & ~(p)->p_sigmask) != 0)
+#define SIGPENDING(p)                                                  \
+       (((p)->p_siglist | (p)->p_p->ps_siglist) & ~(p)->p_sigmask)

 /*
  * Determine signal that should be delivered to process p, the current
  * process, 0 if none.  If there is a pending stop signal with default
  * action, the process stops in issignal().
  */
-#define        CURSIG(p)                                                       
\
-       (((p)->p_siglist == 0 ||                                        \
+#define CURSIG(p)                                                      \
+       ((((p)->p_siglist | (p)->p_p->ps_siglist) == 0 ||               \
            (((p)->p_p->ps_flags & PS_TRACED) == 0 &&                   \
-           ((p)->p_siglist & ~(p)->p_sigmask) == 0)) ?                 \
+           SIGPENDING(p) == 0)) ?                                      \
            0 : issignal(p))

 /*
  * Clear a pending signal from a process.
  */
-#define        CLRSIG(p, sig)  atomic_clearbits_int(&(p)->p_siglist, 
sigmask(sig))
+#define CLRSIG(p, sig) do {                                            \
+       int _mask = sigmask(sig);                                       \
+       atomic_clearbits_int(&(p)->p_siglist, _mask);                   \
+       atomic_clearbits_int(&(p)->p_p->ps_siglist, _mask);             \
+} while (0)

 /*
  * Signal properties and actions.
Index: sys/sysctl.h
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/sys/sysctl.h,v
retrieving revision 1.185
diff -u -p -r1.185 sysctl.h
--- sys/sysctl.h        9 May 2019 14:59:30 -0000       1.185
+++ sys/sysctl.h        10 May 2019 02:39:07 -0000
@@ -595,7 +595,7 @@ do {                                                        
                \
                (kp)->p_tracep = PTRTOINT64((pr)->ps_tracevp);          \
        (kp)->p_traceflag = (pr)->ps_traceflag;                         \
                                                                        \
-       (kp)->p_siglist = (p)->p_siglist;                               \
+       (kp)->p_siglist = (p)->p_siglist | (pr)->ps_siglist;            \
        (kp)->p_sigmask = (p)->p_sigmask;                               \
        (kp)->p_sigignore = (sa) ? (sa)->ps_sigignore : 0;              \
        (kp)->p_sigcatch = (sa) ? (sa)->ps_sigcatch : 0;                \

Reply via email to