Subject: sigwait(2) implementation
To: None <tech-kern@netbsd.org>
From: Jaromir Dolecek <jdolecek@netbsd.org>
List: tech-kern
Date: 01/25/2003 17:25:25
--ELM740045059-10923-0_
Content-Transfer-Encoding: 7bit
Content-Type: text/plain; charset=US-ASCII

Hi,

it occurred to me sigwait(2) could be useful now that we have threads.
So I implemented one, patch is appended to this e-mail[*]
It should work according to how sigwait(2) is specified in SUSv3.
There are two potential issues tho.

First, if there are two different threads wait for a same signal
and the signal arrives, only one thread returns and the second will
keep sleeping forever, even if the signal would arrive again.  This
is since the list of signal being waited for is cleaned on return
from sigwait(), so the info about waiting second thread would be
lost once the first one returns. Behaviour in this case (two threads
wait on same signal) is unspecified in standard, so this should be
okay and it simplifies things. AFAIK apps are not supposed to call
sigwait() from two different threads with same signal.

Second one is potentially more dangerous. If thread sigwaits for
more than one signal and more than one signal actually occurs before
the thread has a chance to pick them up (say, SIGSEGV and SIGBUS,
from interrupt context), only one of the signals would get returned
via sigwait(). The other signal(s) would stay on the ps_sigwaited
list, and not acted upon by normal signal delivery, as they are
supposed to. If the process would call sigwait(2) again with same
args, it would pick up the 'other' signal(s)s from ps_sigwaited list, so they
eventually _would_ be delivered.  I wonder how big is this a problem.

Also, current implementation lacks any locks. This should
work as far as our high kernel is biglock+nonpreemptive.

Opinions?

Jaromir

 [*] The syscall glue in syscall tables and libc is not included in patch.

--ELM740045059-10923-0_
Content-Transfer-Encoding: 7bit
Content-Type: text/plain; charset=ISO-8859-2
Content-Disposition: attachment; filename=addsigwait.txt

Index: kern/kern_sig.c
===================================================================
RCS file: /cvsroot/src/sys/kern/kern_sig.c,v
retrieving revision 1.130
diff -u -p -r1.130 kern_sig.c
--- kern/kern_sig.c	2003/01/18 10:06:29	1.130
+++ kern/kern_sig.c	2003/01/25 13:41:50
@@ -356,6 +356,8 @@ siginit(struct proc *p)
 		SIGACTION_PS(ps, signum).sa_flags = SA_RESTART;
 	}
 	sigemptyset(&p->p_sigctx.ps_sigcatch);
+	sigemptyset(&p->p_sigctx.ps_sigwait);
+	sigemptyset(&p->p_sigctx.ps_sigwaited);
 	p->p_flag &= ~P_NOCLDSTOP;
 
 	/*
@@ -402,6 +404,8 @@ execsigs(struct proc *p)
 		SIGACTION_PS(ps, signum).sa_flags = SA_RESTART;
 	}
 	sigemptyset(&p->p_sigctx.ps_sigcatch);
+	sigemptyset(&p->p_sigctx.ps_sigwait);
+	sigemptyset(&p->p_sigctx.ps_sigwaited);
 	p->p_flag &= ~P_NOCLDSTOP;
 
 	/*
@@ -856,6 +860,22 @@ psignal1(struct proc *p, int signum,
 	p->p_sigctx.ps_sigcheck = 1;
 
 	/*
+	 * If the signal doesn't have SA_CANTMASK (no override for SIGKILL,
+	 * please!), check if anything waits on it. If yes, clear the
+	 * pending signal from siglist set, add it to sigwaited set, and
+	 * wakeup any sigwaiters. The signal won't be processed further
+	 * here.
+	 */
+	if ((prop & SA_CANTMASK) == 0
+	    && sigismember(&p->p_sigctx.ps_sigwait, signum)) {
+		sigdelset(&p->p_sigctx.ps_siglist, signum);
+		sigaddset(&p->p_sigctx.ps_sigwaited, signum);
+
+		wakeup(&p->p_sigctx.ps_sigwait);
+		return;
+	}
+
+	/*
 	 * Defer further processing for signals which are held,
 	 * except that stopped processes must be continued by SIGCONT.
 	 */
@@ -1032,7 +1052,8 @@ psignal1(struct proc *p, int signum,
 			goto out;
 		} else {
 			/* Else what? */
-			panic("psignal: Invalid process state.");
+			panic("psignal: Invalid process state %d.",
+				p->p_stat);
 		}
 	}
 	/*NOTREACHED*/
@@ -1818,6 +1839,79 @@ sys_setcontext(struct lwp *l, void *v, r
 	return (EJUSTRETURN);
 }
 
+int
+sys_sigwait(struct lwp *l, void *v, register_t *retval)
+{
+	struct sys_sigwait_args /* {
+		syscallarg(const sigset_t *) set;
+		syscallarg(int *) signum;
+	} */ *uap = v;
+	sigset_t waitset;
+	struct proc *p = l->l_proc;
+	int error;
+	int sig, signum = 0;
+
+	sigemptyset(&waitset);
+	if ((error = copyin(SCARG(uap, set), &waitset, sizeof(waitset))))
+		return (error);
+
+	/*
+	 * First scan siglist and check if there already is signal from
+	 * our waitlist pending. Ignore CANTMASK signals.
+	 */
+	for(sig=1; sig < _NSIG; sig++) {
+		if ((sigprop[sig] & SA_CANTMASK) == 0
+		    && sigismember(&waitset, sig)
+		    && sigismember(&p->p_sigctx.ps_siglist, sig)) {
+			/* found pending signal */
+			signum = sig;
+			sigdelset(&p->p_sigctx.ps_siglist, sig);
+			goto out;
+		}
+	}
+
+	/*
+	 * Add to ps_sigwait list according to our needs.
+	 * XXXSMP this is only safe with BIG LOCK nonpreemptive kernel
+	 */
+	sigplusset(&waitset, &p->p_sigctx.ps_sigwait);
+		
+	/*
+	 * Loop until we'd either:
+	 * 1. found the signal we want
+	 * 2. would be interrupted by signal ourselves
+	 */
+	for(; error == 0;) {
+		/*
+		 * Check if a signal from our wait set has arrived.
+		 */
+		for(sig=1; sig < _NSIG; sig++) {
+			if (sigismember(&waitset, sig)
+			    && sigismember(&p->p_sigctx.ps_sigwaited, sig)) {
+				/* signal from our wait set has been posted */
+				signum = sig;
+
+				sigdelset(&p->p_sigctx.ps_sigwaited, sig);
+				goto outset;
+			}
+		}
+
+		/* wake us in better times */
+		error = tsleep(&p->p_sigctx.ps_sigwait, PUSER|PCATCH,
+			"sigwait", 0);
+	}
+
+    outset:
+	/* remove our set from waited list */
+	sigminusset(&waitset, &p->p_sigctx.ps_sigwait);
+
+    out:
+	/* If a signal from our wait set arrived, copy it to userland. */
+	if (signum > 0)
+		error = copyout(&signum, SCARG(uap, signum), sizeof(signum));
+
+	return (error);
+}
 
 /*
  * Returns true if signal is ignored or masked for passed process.
Index: sys/signalvar.h
===================================================================
RCS file: /cvsroot/src/sys/sys/signalvar.h,v
retrieving revision 1.37
diff -u -p -r1.37 signalvar.h
--- sys/signalvar.h	2003/01/18 09:53:20	1.37
+++ sys/signalvar.h	2003/01/25 13:41:50
@@ -75,6 +75,8 @@ struct	sigctx {
 	sigset_t ps_sigmask;		/* Current signal mask. */
 	sigset_t ps_sigignore;		/* Signals being ignored. */
 	sigset_t ps_sigcatch;		/* Signals being caught by user. */
+	sigset_t ps_sigwait;		/* Signals being waited for */
+	sigset_t ps_sigwaited;		/* Delivered signals from wait set */
 };
 
 /* signal flags */

--ELM740045059-10923-0_--