Subject: proposed fix for PR 30348 -- sigwait doesn't get signals from pthread_kill
To: None <tech-userlevel@netbsd.org>
From: Chuck Silvers <chuq@chuq.com>
List: tech-userlevel
Date: 10/09/2005 09:09:12
--/WwmFnJnmDyWGHa4
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
hi,
the attached patch fixes the interactions between sigtimedwait() and
pthread_kill(). it allows sigtimedwait() (and its wrappers sigwait()
and sigwaitinfo()) to return properly if a signal is received while
the thread is sleeping or if a signal is pending already when the
function is called. while I was looking, I noticed another place where
we return an error code instead of putting the error code in errno and
returning -1, so I fixed that too.
note that this is not safe for PTHREAD_CONCURRENCY > 1, but there are
various other problems with that already so I didn't want to make this
more complicated than it needed to be.
anyone see any problem with this?
-Chuck
--/WwmFnJnmDyWGHa4
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="diff.kill-vs-sigwait"
Index: pthread_sig.c
===================================================================
RCS file: /cvsroot/src/lib/libpthread/pthread_sig.c,v
retrieving revision 1.42
diff -u -p -r1.42 pthread_sig.c
--- pthread_sig.c 13 Sep 2005 02:45:38 -0000 1.42
+++ pthread_sig.c 9 Oct 2005 15:59:22 -0000
@@ -46,6 +46,7 @@ __RCSID("$NetBSD: pthread_sig.c,v 1.42 2
#define __EXPOSE_STACK 1
#include <sys/param.h>
+#include <sys/types.h>
#include <errno.h>
#include <lwp.h>
#include <stdint.h>
@@ -299,13 +300,13 @@ pthread_sigtimedwait__callback(void *arg
}
int
-sigtimedwait(const sigset_t * __restrict set, siginfo_t * __restrict info, const struct timespec * __restrict timeout)
+sigtimedwait(const sigset_t * __restrict set, siginfo_t * __restrict info,
+ const struct timespec * __restrict timeout)
{
- pthread_t self;
- int error = 0;
- pthread_t target;
+ pthread_t self, target;
sigset_t wset;
struct timespec timo;
+ int sig, error = 0;
/* if threading not started yet, just do the syscall */
if (__predict_false(pthread__started == 0))
@@ -322,8 +323,10 @@ sigtimedwait(const sigset_t * __restrict
}
if (timeout) {
- if ((u_int) timeout->tv_nsec >= 1000000000)
- return (EINVAL);
+ if ((u_int) timeout->tv_nsec >= 1000000000) {
+ errno = EINVAL;
+ return (-1);
+ }
timo = *timeout;
}
@@ -331,6 +334,20 @@ sigtimedwait(const sigset_t * __restrict
pthread_spinlock(self, &pt_sigwaiting_lock);
/*
+ * Check if one of the signals that we will be waiting for is
+ * already pending. If so, return it immediately.
+ */
+ wset = *set;
+ __sigandset(&self->pt_siglist, &wset);
+ sig = firstsig(&wset);
+ if (sig) {
+ info->si_signo = sig;
+ pthread_spinunlock(self, &pt_sigwaiting_lock);
+ pthread__testcancel(self);
+ return 0;
+ }
+
+ /*
* If there is already master thread running, arrange things
* to accomodate for eventual extra signals to wait for,
* and join the sigwaiting list.
@@ -339,6 +356,8 @@ sigtimedwait(const sigset_t * __restrict
struct pt_alarm_t timoalarm;
struct timespec etimo;
+ SDPRINTF(("(stw %p) not master\n", self));
+
/*
* Get current time. We need it if we would become master.
*/
@@ -383,7 +402,9 @@ sigtimedwait(const sigset_t * __restrict
PTQ_INSERT_TAIL(&pt_sigwaiting, self, pt_sleep);
+ SDPRINTF(("(stw %p) not master blocking\n", self));
pthread__block(self, &pt_sigwaiting_lock);
+ SDPRINTF(("(stw %p) not master unblocked\n", self));
/* check if we got a signal we waited for */
if (info->si_signo) {
@@ -443,6 +464,7 @@ sigtimedwait(const sigset_t * __restrict
/* Master thread loop */
pt_sigwmaster = self;
+ SDPRINTF(("(stw %p) i am the master\n", self));
for(;;) {
/* Build our wait set */
wset = *set;
@@ -457,39 +479,50 @@ sigtimedwait(const sigset_t * __restrict
* We are either the only one, or wait set was setup already.
* Just do the syscall now.
*/
+ SDPRINTF(("(stw %p) master blocking\n", self));
error = __sigtimedwait(&wset, info, (timeout) ? &timo : NULL);
+ SDPRINTF(("(stw %p) master unblocking\n", self));
pthread_spinlock(self, &pt_sigwaiting_lock);
if ((error && (errno != ECANCELED || self->pt_cancel))
|| (!error && __sigismember14(set, info->si_signo)) ) {
+
/*
* Normal function return. Clear pt_sigwmaster,
* and if wait queue is nonempty, make first waiter
* new master.
*/
+ SDPRINTF(("(stw %p) master normal self %d\n",
+ self, info->si_signo));
pt_sigwmaster = NULL;
if (!PTQ_EMPTY(&pt_sigwaiting)) {
pt_sigwmaster = PTQ_FIRST(&pt_sigwaiting);
+ SDPRINTF(("(stw %p) new master %p\n", self,
+ pt_sigwmaster));
PTQ_REMOVE(&pt_sigwaiting, pt_sigwmaster,
pt_sleep);
pthread__sched(self, pt_sigwmaster);
}
pthread_spinunlock(self, &pt_sigwaiting_lock);
-
pthread__testcancel(self);
return (error);
}
if (!error) {
+
/*
* Got a signal, but not from _our_ wait set.
* Scan the queue of sigwaiters and wakeup
* the first thread waiting for this signal.
*/
PTQ_FOREACH(target, &pt_sigwaiting, pt_sleep) {
- if (__sigismember14(target->pt_sigwait, info->si_signo)) {
- pthread__assert(target->pt_state == PT_STATE_BLOCKED_QUEUE);
+ if (__sigismember14(target->pt_sigwait,
+ info->si_signo)) {
+ pthread__assert(target->pt_state ==
+ PT_STATE_BLOCKED_QUEUE);
+ SDPRINTF(("(stw %p) master target %p\n",
+ self, target));
/* copy to waiter siginfo */
memcpy(target->pt_wsig, info, sizeof(*info));
@@ -500,6 +533,7 @@ sigtimedwait(const sigset_t * __restrict
}
if (!target) {
+
/*
* Didn't find anyone waiting on this signal.
* Deliver signal normally. This might
@@ -507,11 +541,14 @@ sigtimedwait(const sigset_t * __restrict
* 'their' signal arrives before the master
* thread would be scheduled after _lwp_wakeup().
*/
+ SDPRINTF(("(stw %p) master orphaned\n", self));
pthread__signal(self, NULL, info);
} else {
+
/*
* Signal waiter removed, adjust our wait set.
*/
+ SDPRINTF(("(stw %p) master raced\n", self));
wset = *set;
PTQ_FOREACH(target, &pt_sigwaiting, pt_sleep)
__sigplusset(target->pt_sigwait, &wset);
@@ -787,14 +824,38 @@ pthread__kill_self(pthread_t self, sigin
static void
pthread__kill(pthread_t self, pthread_t target, siginfo_t *si)
{
+ int deliver;
SDPRINTF(("(pthread__kill %p) target %p sig %d code %d\n", self, target,
si->si_signo, si->si_code));
if (__sigismember14(&target->pt_sigmask, si->si_signo)) {
- /* Record the signal for later delivery. */
- __sigaddset14(&target->pt_siglist, si->si_signo);
- return;
+ SDPRINTF(("(pthread__kill %p) target masked\n", target));
+
+ /*
+ * If the target is waiting for this signal in sigtimedwait(),
+ * make the target runnable but do not deliver the signal.
+ * Otherwise record the signal for later delivery.
+ * XXX not MPsafe.
+ */
+ pthread_spinlock(self, &self->pt_statelock);
+ if (target->pt_state == PT_STATE_BLOCKED_QUEUE &&
+ target->pt_sleepq == &pt_sigwaiting &&
+ __sigismember14(target->pt_sigwait, si->si_signo)) {
+ SDPRINTF(("(pthread__kill %p) stw\n", target));
+ target->pt_wsig->si_signo = si->si_signo;
+ pthread_spinunlock(self, &self->pt_statelock);
+ deliver = 0;
+ } else {
+ SDPRINTF(("(pthread__kill %p) deferring\n", target));
+ pthread_spinunlock(self, &self->pt_statelock);
+ __sigaddset14(&target->pt_siglist, si->si_signo);
+ return;
+ }
+ } else {
+ SDPRINTF(("(pthread__kill %p) target %p delivering\n",
+ self, target));
+ deliver = 1;
}
if (self == target) {
@@ -818,6 +879,8 @@ pthread__kill(pthread_t self, pthread_t
*/
pthread_spinlock(self, &target->pt_statelock);
if (target->pt_blockgen != target->pt_unblockgen) {
+ SDPRINTF(("(pthread__kill %p) target running\n", target));
+
/*
* The target is not on a queue at all, and
* won't run again for a while. Try to wake it
@@ -833,6 +896,8 @@ pthread__kill(pthread_t self, pthread_t
_lwp_wakeup(target->pt_blockedlwp);
return;
}
+ SDPRINTF(("(pthread__kill %p) target state %d\n", target,
+ target->pt_state));
switch (target->pt_state) {
case PT_STATE_SUSPENDED:
pthread_spinlock(self, &pthread__runqueue_lock);
@@ -853,7 +918,8 @@ pthread__kill(pthread_t self, pthread_t
;
}
- pthread__deliver_signal(self, target, si);
+ if (deliver)
+ pthread__deliver_signal(self, target, si);
pthread__sched(self, target);
pthread_spinunlock(self, &target->pt_statelock);
}
--/WwmFnJnmDyWGHa4--