Source-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[src/trunk]: src/lib/libpthread interposition sigtimedwait() with a thread-aw...
details: https://anonhg.NetBSD.org/src/rev/24852a7f846a
branches: trunk
changeset: 543060:24852a7f846a
user: jdolecek <jdolecek%NetBSD.org@localhost>
date: Sat Feb 15 21:18:59 2003 +0000
description:
interposition sigtimedwait() with a thread-aware version, which uses
single proxy thread to do the actual syscall, and blocks other threads
in userland
diffstat:
lib/libpthread/pthread_int.h | 4 +-
lib/libpthread/pthread_sig.c | 266 ++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 266 insertions(+), 4 deletions(-)
diffs (truncated from 326 to 300 lines):
diff -r f1ead594b063 -r 24852a7f846a lib/libpthread/pthread_int.h
--- a/lib/libpthread/pthread_int.h Sat Feb 15 21:14:11 2003 +0000
+++ b/lib/libpthread/pthread_int.h Sat Feb 15 21:18:59 2003 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: pthread_int.h,v 1.7 2003/02/15 04:34:40 nathanw Exp $ */
+/* $NetBSD: pthread_int.h,v 1.8 2003/02/15 21:18:59 jdolecek Exp $ */
/*-
* Copyright (c) 2001 The NetBSD Foundation, Inc.
@@ -103,6 +103,8 @@
sigset_t pt_sigmask; /* Signals we won't take. */
sigset_t pt_siglist; /* Signals pending for us. */
sigset_t pt_sigblocked; /* Signals delivered while blocked. */
+ sigset_t *pt_sigwait; /* Signals waited for in sigwait */
+ siginfo_t *pt_wsig;
pthread_spin_t pt_siglock; /* Lock on above */
void * pt_exitval; /* Read by pthread_join() */
diff -r f1ead594b063 -r 24852a7f846a lib/libpthread/pthread_sig.c
--- a/lib/libpthread/pthread_sig.c Sat Feb 15 21:14:11 2003 +0000
+++ b/lib/libpthread/pthread_sig.c Sat Feb 15 21:18:59 2003 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: pthread_sig.c,v 1.8 2003/02/15 04:38:58 nathanw Exp $ */
+/* $NetBSD: pthread_sig.c,v 1.9 2003/02/15 21:19:00 jdolecek Exp $ */
/*-
* Copyright (c) 2001 The NetBSD Foundation, Inc.
@@ -86,6 +86,11 @@
*/
static pthread_cond_t pt_sigsuspended_cond = PTHREAD_COND_INITIALIZER;
+/* Queue of threads that are waiting in sigtimedwait(). */
+static struct pthread_queue_t pt_sigwaiting;
+static pthread_spin_t pt_sigwaiting_lock;
+static pthread_t pt_sigwmaster;
+static pthread_cond_t pt_sigwaiting_cond = PTHREAD_COND_INITIALIZER;
static void pthread__kill(pthread_t, pthread_t, int, int);
static void pthread__kill_self(pthread_t, int, int);
@@ -94,6 +99,8 @@
pthread__signal_tramp(int, int, void (*)(int, int, struct sigcontext *),
ucontext_t *, sigset_t *);
+static int firstsig(const sigset_t *);
+
__strong_alias(__libc_thr_sigsetmask,pthread_sigmask)
void
@@ -101,6 +108,9 @@
{
SDPRINTF(("(signal_init) setting process sigmask\n"));
__sigprocmask14(0, NULL, &pt_process_sigmask);
+
+ PTQ_INIT(&pt_sigsuspended);
+ PTQ_INIT(&pt_sigwaiting);
}
int
@@ -146,8 +156,8 @@
/*
- * Interpositioning is our friend. We need to intercept sigaction() and
- * sigsuspend().
+ * Interpositioning is our friend. We need to intercept sigaction(),
+ * sigsuspend() and sigtimedwait().
*/
int
@@ -223,6 +233,256 @@
}
/*
+ * Interpositioned sigtimedwait(2), need to multiplex all
+ * eventual callers to a single kernel lwp.
+ */
+int _sigtimedwait(const sigset_t * __restrict, siginfo_t * __restrict,
+ const struct timespec * __restrict);
+
+static void
+pthread_sigtimedwait__callback(void *arg)
+{
+ pthread__sched(pthread__self(), (pthread_t) arg);
+}
+
+int
+sigtimedwait(const sigset_t * __restrict set, siginfo_t * __restrict info, const struct timespec * __restrict timeout)
+{
+ pthread_t self;
+ int error = 0;
+ pthread_t target;
+ sigset_t wset;
+ extern int pthread__started;
+ struct timespec timo;
+
+ /* if threading not started yet, just do the syscall */
+ if (__predict_false(pthread__started == 0))
+ return (_sigtimedwait(set, info, timeout));
+
+ self = pthread__self();
+ pthread__testcancel(self);
+
+ /* also call syscall if timeout is zero (i.e. polling) */
+ if (timeout && timeout->tv_sec == 0 && timeout->tv_nsec == 0) {
+ error = _sigtimedwait(set, info, timeout);
+ pthread__testcancel(self);
+ return (error);
+ }
+
+ if (timeout) {
+ if ((u_int) timeout->tv_nsec >= 1000000000)
+ return (EINVAL);
+
+ timo = *timeout;
+ }
+
+ pthread_spinlock(self, &pt_sigwaiting_lock);
+
+ /*
+ * If there is already master thread running, arrange things
+ * to accomodate for eventual extra signals to wait for,
+ * and join the sigwaiting list.
+ */
+ if (pt_sigwmaster) {
+ struct pt_alarm_t timoalarm;
+ struct timespec etimo;
+
+ /*
+ * Get current time. We need it if we would become master.
+ */
+ if (timeout) {
+ clock_gettime(CLOCK_MONOTONIC, &etimo);
+ timespecadd(&etimo, timeout, &etimo);
+ }
+
+ /*
+ * Check if this thread's wait set is different to master set.
+ */
+ wset = *set;
+ __sigminusset(pt_sigwmaster->pt_sigwait, &wset);
+ if (firstsig(&wset)) {
+ /*
+ * Some new signal in set, wakeup master. It will
+ * rebuild its wait set.
+ */
+ _lwp_wakeup(pt_sigwmaster->pt_blockedlwp);
+ }
+
+ /* Save our wait set and info pointer */
+ wset = *set;
+ self->pt_sigwait = &wset;
+ self->pt_wsig = info;
+
+ /* zero to recognize when we get passed the signal from master */
+ info->si_signo = 0;
+
+ if (timeout) {
+ pthread__alarm_add(self, &timoalarm, &etimo,
+ pthread_sigtimedwait__callback, self);
+ }
+
+ block:
+ pthread_spinlock(self, &self->pt_statelock);
+ self->pt_state = PT_STATE_BLOCKED_QUEUE;
+ self->pt_sleepobj = &pt_sigwaiting_cond;
+ self->pt_sleepq = &pt_sigwaiting;
+ self->pt_sleeplock = &pt_sigwaiting_lock;
+ pthread_spinunlock(self, &self->pt_statelock);
+
+ PTQ_INSERT_TAIL(&pt_sigwaiting, self, pt_sleep);
+
+ pthread__block(self, &pt_sigwaiting_lock);
+
+ /* check if we got a signal we waited for */
+ if (info->si_signo) {
+ /* got the signal from master */
+ pthread__testcancel(self);
+ return (0);
+ }
+
+ /* need the lock from now on */
+ pthread_spinlock(self, &pt_sigwaiting_lock);
+
+ /*
+ * If alarm fired, remove us from queue, adjust master
+ * wait set and return with EAGAIN.
+ */
+ if (timeout) {
+ if (pthread__alarm_fired(&timoalarm)) {
+ PTQ_REMOVE(&pt_sigwaiting, self, pt_sleep);
+
+ /*
+ * Signal master. It will rebuild it's wait set.
+ */
+ _lwp_wakeup(pt_sigwmaster->pt_blockedlwp);
+
+ pthread_spinunlock(self, &pt_sigwaiting_lock);
+ errno = EAGAIN;
+ return (-1);
+ }
+ pthread__alarm_del(self, &timoalarm);
+ }
+
+ /*
+ * May have been woken up to deliver signal - check if we are
+ * the master and reblock if appropriate.
+ */
+ if (pt_sigwmaster != self)
+ goto block;
+
+ /* not signal nor alarm, must have been upgraded to master */
+ assert(pt_sigwmaster == self);
+
+ /* update timeout before upgrading to master */
+ if (timeout) {
+ struct timespec tnow;
+
+ clock_gettime(CLOCK_MONOTONIC, &tnow);
+ /* compute difference to end time */
+ timespecsub(&tnow, &etimo, &tnow);
+ /* substract the difference from timeout */
+ timespecsub(&timo, &tnow, &timo);
+ }
+ }
+
+ /* MASTER */
+ self->pt_sigwait = &wset;
+ self->pt_wsig = NULL;
+
+ /* Master thread loop */
+ pt_sigwmaster = self;
+ for(;;) {
+ /* Build our wait set */
+ wset = *set;
+ if (!PTQ_EMPTY(&pt_sigwaiting)) {
+ PTQ_FOREACH(target, &pt_sigwaiting, pt_sleep)
+ __sigplusset(target->pt_sigwait, &wset);
+ }
+
+ pthread_spinunlock(self, &pt_sigwaiting_lock);
+
+ /*
+ * We are either the only one, or wait set was setup already.
+ * Just do the syscall now.
+ */
+ error = __sigtimedwait(&wset, info, (timeout) ? &timo : NULL);
+
+ pthread_spinlock(self, &pt_sigwaiting_lock);
+ if ((error && errno != ECANCELED)
+ || (!error && __sigismember14(set, info->si_signo)) ) {
+ /*
+ * Normal function return. Clear pt_sigwmaster,
+ * and if wait queue is nonempty, make first waiter
+ * new master.
+ */
+ pt_sigwmaster = NULL;
+ if (!PTQ_EMPTY(&pt_sigwaiting)) {
+ pt_sigwmaster = PTQ_FIRST(&pt_sigwaiting);
+ 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)) {
+ assert(target->pt_state==PT_STATE_BLOCKED_QUEUE);
+
+ /* copy to waiter siginfo */
+ memcpy(target->pt_wsig, info, sizeof(*info));
+ PTQ_REMOVE(&pt_sigwaiting, target, pt_sleep);
+ pthread__sched(self, target);
+ break;
+ }
+ }
+
+ if (!target) {
+ /*
+ * Didn't find anyone waiting on this signal.
+ * Deliver signal normally. This might
+ * happen if a thread times out, but
+ * 'their' signal arrives before the master
+ * thread would be scheduled after _lwp_wakeup().
+ */
+ pthread__signal(self, NULL, info->si_signo,
+ info->si_code);
+ } else {
+ /*
+ * Signal waiter removed, adjust our wait set.
Home |
Main Index |
Thread Index |
Old Index