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