bug-hurd
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH 8/8] Hurd signals: implement global signal dispositions


From: Jeremie Koenig
Subject: [PATCH 8/8] Hurd signals: implement global signal dispositions
Date: Wed, 25 May 2011 17:59:34 +0200

Currently each thread has a full "sigstate" structure which keeps track
of the signal dispositions, blocking mask and pending signals for this
thread. Process-wide signals are delivered to the main thread.

However, the semantics for POSIX threads is that all of them share the
same signal dispositions, although their blocking masks may differ.
Signals sent to the process as a whole can be delivered to any thread
which does not block them.

This is implemented here in addition to the current behavior: libpthread
will call _hurd_sigstate_set_global_rcv to mark newly created threads as
global signal receivers, while cthreads-based programs can continue to
rely on the Hurd semantics.

* hurd/hurdsig.c: Replace _hurd_sigthread with a _hurd_global_sigstate.
  (_hurd_thread_sigstate): accept MACH_PORT_NULL as the global sigstate's
  thread.  (_hurd_sigstate_set_global_rcv): New function to mark some
  threads as global signal receiver.  (sigstate_is_global_rcv): New
  function, test a thread's status as a global receiver.
  (_hurd_sigstate_lock, _hurd_sigstate_unlock): New function, lock a
  sigstate structure and possibly _hurd_global_sigstate, so that the set
  of pending signals can be queried safely.  (sigstate_clear_pending): New
  function, pulls a signal out of a thread's pending mask.
  (_hurd_sigstate_pending): New function, retreive a thread's pending mask,
  taking into account its status as a global receiver.
  (_hurd_sigstate_actions): New function, retreive a thread's actions
  vector, taking into account its status as a global receiver.
  (abort_thread): Use _hurd_sigstate_actions.  (post_signal, post_pending,
  post_all_pending_signals, _hurd_internal_post_signal): Take into account
  the possibility of ss == _hurd_global_sigstate.  Use _hurd_sigstate_lock,
  _hurd_sigstate_action and _hurd_sigstate_pending.  (_S_msg_sig_post,
  _S_msg_sig_post_untraced): Post signals against _hurd_global_sigstate
  instead of _hurd_sigthread.  (_hurdsig_init): Initialize
  _hurd_global_sigstate.
* hurd/hurd/signal.h: Document the new behavior of hurd_sigstate.actions.
  Declare the new symbols _hurd_global_sigstate, _hurd_sigstate_set_global_rcv,
  _hurd_sigstate_lock, _hurd_sigstate_actions, _hurd_sigstate_pending and
  _hurd_sigstate_unlock.  (_hurd_critical_section_unlock): Update querying
  of the pending mask.
* hurd/hurdmsg.c (get_int): Replace _hurd_sigthread with _hurd_global_sigstate.
* sysdeps/mach/hurd/fork.c (__fork): Mark the main thread as a global receiver.
  Reclaim _hurd_global_sigstate along the with main thread's sigstate.
* sysdeps/mach/hurd/sigaction.c (__sigaction), sysdeps/mach/hurd/sigpending.c
  (__sigpending), sysdeps/mach/hurd/sigprocmask.c (__sigprocmask),
  sysdeps/mach/hurd/sigsuspend.c (__sigsuspend), sysdeps/mach/hurd/sigwait.c
  (__sigwait), sysdeps/mach/hurd/spawni.c (__spawni): Use the new accessors
  to query sigstate pending masks and action vectors.
* Versions.def: Add the new version GLIBC_2.13.
* hurd/Versions: Export the new symbols _hurd_sigstate_set_global_rcv,
  _hurd_sigstate_lock, _hurd_sigstate_pending and _hurd_sigstate_unlock,
  for libpthread's benefit.
---
 Versions.def                    |    1 +
 hurd/Versions                   |    7 ++
 hurd/hurd/signal.h              |   35 +++++--
 hurd/hurdmsg.c                  |   12 +-
 hurd/hurdsig.c                  |  192 ++++++++++++++++++++++++++++-----------
 sysdeps/mach/hurd/fork.c        |   19 ++--
 sysdeps/mach/hurd/sigaction.c   |   16 ++--
 sysdeps/mach/hurd/sigpending.c  |    6 +-
 sysdeps/mach/hurd/sigprocmask.c |    8 +-
 sysdeps/mach/hurd/sigsuspend.c  |   15 ++--
 sysdeps/mach/hurd/sigwait.c     |   11 +-
 sysdeps/mach/hurd/spawni.c      |   23 +++--
 12 files changed, 231 insertions(+), 114 deletions(-)

diff --git a/Versions.def b/Versions.def
index eab006b..0ccda50 100644
--- a/Versions.def
+++ b/Versions.def
@@ -2,60 +2,61 @@ libBrokenLocale {
   GLIBC_2.0
 }
 libc {
   GLIBC_2.0
   GLIBC_2.1
   GLIBC_2.1.1
   GLIBC_2.1.2
   GLIBC_2.1.3
   GLIBC_2.1.4
   GLIBC_2.2
   GLIBC_2.2.1
   GLIBC_2.2.2
   GLIBC_2.2.3
   GLIBC_2.2.4
   GLIBC_2.2.5
   GLIBC_2.2.6
   GLIBC_2.3
   GLIBC_2.3.1
   GLIBC_2.3.2
   GLIBC_2.3.3
   GLIBC_2.3.4
   GLIBC_2.4
   GLIBC_2.5
   GLIBC_2.6
   GLIBC_2.7
   GLIBC_2.8
   GLIBC_2.9
   GLIBC_2.10
   GLIBC_2.11
   GLIBC_2.12
+  GLIBC_2.13
 %ifdef USE_IN_LIBIO
   HURD_CTHREADS_0.3
 %endif
 %ifdef EXPORT_UNWIND_FIND_FDE
   GCC_3.0
 %endif
   GLIBC_PRIVATE
 }
 libcrypt {
   GLIBC_2.0
 }
 libdl {
   GLIBC_2.0
   GLIBC_2.1
   GLIBC_2.3.3
   GLIBC_2.3.4
 }
 libm {
   GLIBC_2.0
   GLIBC_2.1
   GLIBC_2.2
   GLIBC_2.2.3
   GLIBC_2.3.4
   GLIBC_2.4
 }
 libnsl {
   GLIBC_2.0
   GLIBC_2.1
   GLIBC_2.2
 }
diff --git a/hurd/Versions b/hurd/Versions
index 83c8ab1..ba87b23 100644
--- a/hurd/Versions
+++ b/hurd/Versions
@@ -129,45 +129,52 @@ libc {
     geteuids;
 
     # s*
     seteuids;
   }
   GLIBC_2.1.3 {
     # d*
     directory_name_split;
 
     # h*
     hurd_directory_name_split;
   }
   GLIBC_2.2.5 {
     # These always existed as inlines but the real functions were not exported.
     __hurd_fail;
     _hurd_self_sigstate;
 
     # Same for these "quasi-internal" functions
     _hurd_port_init;
     _hurd_port_set;
 
     # internal symbols used by other libraries (e.g. librt)
     _hurd_raise_signal;
     _hurdsig_interrupt_timeout;
     _hurdsig_fault_preemptor; _hurdsig_fault_env;
   }
   GLIBC_2.2.6 {
     # functions used in macros & inline functions
     __errno_location;
   }
+  GLIBC_2.13 {
+    # functions used by libpthread
+    _hurd_sigstate_set_global_rcv;
+    _hurd_sigstate_lock;
+    _hurd_sigstate_pending;
+    _hurd_sigstate_unlock;
+  }
 
 %if !SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_2)
   HURD_CTHREADS_0.3 {
     # weak refs to libthreads functions that libc calls iff libthreads in use
     cthread_fork; cthread_detach;
 
     # variables used for detecting cthreads
     _cthread_exit_routine; _cthread_init_routine;
 
     # cthreads functions with stubs in libc
     cthread_keycreate; cthread_getspecific; cthread_setspecific;
     __libc_getspecific;
   }
 %endif
 }
diff --git a/hurd/hurd/signal.h b/hurd/hurd/signal.h
index 46c1fe1..247328c 100644
--- a/hurd/hurd/signal.h
+++ b/hurd/hurd/signal.h
@@ -44,205 +44,222 @@
 #include <spin-lock.h>
 #include <hurd/threadvar.h>    /* We cache sigstate in a threadvar.  */
 struct hurd_signal_preemptor;  /* <hurd/sigpreempt.h> */
 
 
 /* Full details of a signal.  */
 struct hurd_signal_detail
   {
     /* Codes from origination Mach exception_raise message.  */
     integer_t exc, exc_code, exc_subcode;
     /* Sigcode as passed or computed from exception codes.  */
     integer_t code;
     /* Error code as passed or extracted from exception codes.  */
     error_t error;
   };
 
 
 /* Per-thread signal state.  */
 
 struct hurd_sigstate
   {
     spin_lock_t critical_section_lock; /* Held if in critical section.  */
 
     spin_lock_t lock;          /* Locks most of the rest of the structure.  */
 
     thread_t thread;
     struct hurd_sigstate *next; /* Linked-list of thread sigstates.  */
 
     sigset_t blocked;          /* What signals are blocked.  */
     sigset_t pending;          /* Pending signals, possibly blocked.  */
+
+    /* Signal handlers.  ACTIONS[0] is used to mark the threads with POSIX
+       semantics: if sa_handler is SIG_IGN instead of SIG_DFL, this thread
+       will receive global signals and use the process-wide action vector
+       instead of this one.  */
     struct sigaction actions[NSIG];
+
     struct sigaltstack sigaltstack;
 
     /* Chain of thread-local signal preemptors; see <hurd/sigpreempt.h>.
        Each element of this chain is in local stack storage, and the chain
        parallels the stack: the head of this chain is in the innermost
        stack frame, and each next element in an outermore frame.  */
     struct hurd_signal_preemptor *preemptors;
 
     /* For each signal that may be pending, the details to deliver it with.  */
     struct hurd_signal_detail pending_data[NSIG];
 
     /* If `suspended' is set when this thread gets a signal,
        the signal thread sends an empty message to it.  */
     mach_port_t suspended;
 
     /* The following members are not locked.  They are used only by this
        thread, or by the signal thread with this thread suspended.  */
 
     volatile mach_port_t intr_port; /* Port interruptible RPC was sent on.  */
 
     /* If this is not null, the thread is in sigreturn awaiting delivery of
        pending signals.  This context (the machine-dependent portions only)
        will be passed to sigreturn after running the handler for a pending
        signal, instead of examining the thread state.  */
     struct sigcontext *context;
 
     /* This is the head of the thread's list of active resources; see
        <hurd/userlink.h> for details.  This member is only used by the
        thread itself, and always inside a critical section.  */
     struct hurd_userlink *active_resources;
 
     /* These are locked normally.  */
     int cancel;                        /* Flag set by hurd_thread_cancel.  */
     void (*cancel_hook) (void);        /* Called on cancellation.  */
   };
 
 /* Linked list of states of all threads whose state has been asked for.  */
 
 extern struct hurd_sigstate *_hurd_sigstates;
 
 extern struct mutex _hurd_siglock; /* Locks _hurd_sigstates.  */
 
 /* Get the sigstate of a given thread, taking its lock.  */
 
 extern struct hurd_sigstate *_hurd_thread_sigstate (thread_t);
 
 /* Get the sigstate of the current thread.
    This uses a per-thread variable to optimize the lookup.  */
 
 extern struct hurd_sigstate *_hurd_self_sigstate (void)
      /* This declaration tells the compiler that the value is constant.
        We assume this won't be called twice from the same stack frame
        by different threads.  */
      __attribute__ ((__const__));
 
+/* Process-wide signal state.  */
+
+struct hurd_sigstate *_hurd_global_sigstate;
+
+/* Mark the given thread as a process-wide signal receiver.  */
+
+extern void _hurd_sigstate_set_global_rcv (struct hurd_sigstate *ss);
+
+/* A thread can either use its own action vector and pending signal set
+   or use the global ones, depending on wether it has been marked as a
+   global receiver. The accessors below take that into account.  */
+
+extern void _hurd_sigstate_lock (struct hurd_sigstate *ss);
+extern struct sigaction *_hurd_sigstate_actions (struct hurd_sigstate *ss);
+extern sigset_t _hurd_sigstate_pending (const struct hurd_sigstate *ss);
+extern void _hurd_sigstate_unlock (struct hurd_sigstate *ss);
+
 #ifndef _HURD_SIGNAL_H_EXTERN_INLINE
 #define _HURD_SIGNAL_H_EXTERN_INLINE __extern_inline
 #endif
 
 #ifdef __USE_EXTERN_INLINES
 _HURD_SIGNAL_H_EXTERN_INLINE struct hurd_sigstate *
 _hurd_self_sigstate (void)
 {
   struct hurd_sigstate **location = (struct hurd_sigstate **)
     (void *) __hurd_threadvar_location (_HURD_THREADVAR_SIGSTATE);
   if (*location == NULL)
     *location = _hurd_thread_sigstate (__mach_thread_self ());
   return *location;
 }
 #endif
 
 /* Thread listening on our message port; also called the "signal thread".  */
 
 extern thread_t _hurd_msgport_thread;
 
 /* Our message port.  We hold the receive right and _hurd_msgport_thread
    listens for messages on it.  We also hold a send right, for convenience.  */
 
 extern mach_port_t _hurd_msgport;
 
-
-/* Thread to receive process-global signals.  */
-
-extern thread_t _hurd_sigthread;
-
-
 /* Resource limit on core file size.  Enforced by hurdsig.c.  */
 extern int _hurd_core_limit;
 
 /* Critical sections.
 
    A critical section is a section of code which cannot safely be interrupted
    to run a signal handler; for example, code that holds any lock cannot be
    interrupted lest the signal handler try to take the same lock and
    deadlock result.  */
 
 void *_hurd_critical_section_lock (void);
 
 #ifdef __USE_EXTERN_INLINES
 _HURD_SIGNAL_H_EXTERN_INLINE void *
 _hurd_critical_section_lock (void)
 {
   struct hurd_sigstate **location = (struct hurd_sigstate **)
     (void *) __hurd_threadvar_location (_HURD_THREADVAR_SIGSTATE);
   struct hurd_sigstate *ss = *location;
   if (ss == NULL)
     {
       /* The thread variable is unset; this must be the first time we've
         asked for it.  In this case, the critical section flag cannot
         possible already be set.  Look up our sigstate structure the slow
         way; this locks the sigstate lock.  */
       ss = *location = _hurd_thread_sigstate (__mach_thread_self ());
       __spin_unlock (&ss->lock);
     }
 
   if (! __spin_try_lock (&ss->critical_section_lock))
     /* We are already in a critical section, so do nothing.  */
     return NULL;
 
   /* With the critical section lock held no signal handler will run.
      Return our sigstate pointer; this will be passed to
      _hurd_critical_section_unlock to unlock it.  */
   return ss;
 }
 #endif
 
 void _hurd_critical_section_unlock (void *our_lock);
 
 #ifdef __USE_EXTERN_INLINES
 _HURD_SIGNAL_H_EXTERN_INLINE void
 _hurd_critical_section_unlock (void *our_lock)
 {
   if (our_lock == NULL)
     /* The critical section lock was held when we began.  Do nothing.  */
     return;
   else
     {
       /* It was us who acquired the critical section lock.  Unlock it.  */
       struct hurd_sigstate *ss = (struct hurd_sigstate *) our_lock;
       sigset_t pending;
-      __spin_lock (&ss->lock);
+      _hurd_sigstate_lock (ss);
       __spin_unlock (&ss->critical_section_lock);
-      pending = ss->pending & ~ss->blocked;
-      __spin_unlock (&ss->lock);
+      pending = _hurd_sigstate_pending(ss) & ~ss->blocked;
+      _hurd_sigstate_unlock (ss);
       if (! __sigisemptyset (&pending))
        /* There are unblocked signals pending, which weren't
           delivered because we were in the critical section.
           Tell the signal thread to deliver them now.  */
        __msg_sig_post (_hurd_msgport, 0, 0, __mach_task_self ());
     }
 }
 #endif
 
 /* Convenient macros for simple uses of critical sections.
    These two must be used as a pair at the same C scoping level.  */
 
 #define HURD_CRITICAL_BEGIN \
   { void *__hurd_critical__ = _hurd_critical_section_lock ()
 #define HURD_CRITICAL_END \
       _hurd_critical_section_unlock (__hurd_critical__); } while (0)
 
 /* Initialize the signal code, and start the signal thread.
    Arguments give the "init ints" from exec_startup.  */
 
 extern void _hurdsig_init (const int *intarray, size_t intarraysize);
 
 /* Initialize proc server-assisted fault recovery for the signal thread.  */
 
 extern void _hurdsig_fault_init (void);
 
 /* Raise a signal as described by SIGNO an DETAIL, on the thread whose
    sigstate SS points to.  If SS is a null pointer, this instead affects
    the calling thread.  */
 
diff --git a/hurd/hurdmsg.c b/hurd/hurdmsg.c
index ffcce61..81bafa6 100644
--- a/hurd/hurdmsg.c
+++ b/hurd/hurdmsg.c
@@ -97,77 +97,77 @@ _S_msg_set_init_ports (mach_port_t msgport, mach_port_t 
auth,
                       mach_port_t *ports, mach_msg_type_number_t nports)
 {
   mach_msg_type_number_t i;
   error_t err;
 
   AUTHCHECK;
 
   for (i = 0; i < _hurd_nports; ++i)
     {
       if (err = _hurd_ports_set (i, ports[i]))
        return err;
       else
        __mach_port_deallocate (__mach_task_self (), ports[i]);
     }
 
   return 0;
 }
 
 /* Snarfing and frobbing the init ints.  */
 
 static kern_return_t
 get_int (int which, int *value)
 {
   switch (which)
     {
     case INIT_UMASK:
       *value = _hurd_umask;
       return 0;
     case INIT_SIGMASK:
       {
-       struct hurd_sigstate *ss = _hurd_thread_sigstate (_hurd_sigthread);
+       struct hurd_sigstate *ss = _hurd_global_sigstate;
        __spin_lock (&ss->lock);
        *value = ss->blocked;
        __spin_unlock (&ss->lock);
        return 0;
       }
     case INIT_SIGPENDING:
       {
-       struct hurd_sigstate *ss = _hurd_thread_sigstate (_hurd_sigthread);
+       struct hurd_sigstate *ss = _hurd_global_sigstate;
        __spin_lock (&ss->lock);
        *value = ss->pending;
        __spin_unlock (&ss->lock);
        return 0;
       }
     case INIT_SIGIGN:
       {
-       struct hurd_sigstate *ss = _hurd_thread_sigstate (_hurd_sigthread);
+       struct hurd_sigstate *ss = _hurd_global_sigstate;
        sigset_t ign;
        int sig;
        __spin_lock (&ss->lock);
        __sigemptyset (&ign);
        for (sig = 1; sig < NSIG; ++sig)
          if (ss->actions[sig].sa_handler == SIG_IGN)
            __sigaddset (&ign, sig);
        __spin_unlock (&ss->lock);
        *value = ign;
        return 0;
       }
     default:
       return EINVAL;
     }
 }
 
 kern_return_t
 _S_msg_get_init_int (mach_port_t msgport, mach_port_t auth,
                     int which, int *value)
 {
   AUTHCHECK;
 
   return get_int (which, value);
 }
 
 kern_return_t
 _S_msg_get_init_ints (mach_port_t msgport, mach_port_t auth,
                      int **values, mach_msg_type_number_t *nvalues)
 {
   error_t err;
@@ -183,77 +183,77 @@ _S_msg_get_init_ints (mach_port_t msgport, mach_port_t 
auth,
   for (i = 0; i < INIT_INT_MAX; ++i)
     switch (err = get_int (i, &(*values)[i]))
       {
       case 0:                  /* Success.  */
        break;
       case EINVAL:             /* Unknown index.  */
        (*values)[i] = 0;
        break;
       default:                 /* Lossage.  */
        __vm_deallocate (__mach_task_self (),
                         (vm_address_t) *values, INIT_INT_MAX * sizeof (int));
        return err;
       }
 
   return 0;
 }
 
 
 static kern_return_t
 set_int (int which, int value)
 {
   switch (which)
     {
     case INIT_UMASK:
       _hurd_umask = value;
       return 0;
 
       /* These are pretty odd things to do.  But you asked for it.  */
     case INIT_SIGMASK:
       {
-       struct hurd_sigstate *ss = _hurd_thread_sigstate (_hurd_sigthread);
+       struct hurd_sigstate *ss = _hurd_global_sigstate;
        __spin_lock (&ss->lock);
        ss->blocked = value;
        __spin_unlock (&ss->lock);
        return 0;
       }
     case INIT_SIGPENDING:
       {
-       struct hurd_sigstate *ss = _hurd_thread_sigstate (_hurd_sigthread);
+       struct hurd_sigstate *ss = _hurd_global_sigstate;
        __spin_lock (&ss->lock);
        ss->pending = value;
        __spin_unlock (&ss->lock);
        return 0;
       }
     case INIT_SIGIGN:
       {
-       struct hurd_sigstate *ss = _hurd_thread_sigstate (_hurd_sigthread);
+       struct hurd_sigstate *ss = _hurd_global_sigstate;
        int sig;
        const sigset_t ign = value;
        __spin_lock (&ss->lock);
        for (sig = 1; sig < NSIG; ++sig)
          {
            if (__sigismember (&ign, sig))
              ss->actions[sig].sa_handler = SIG_IGN;
            else if (ss->actions[sig].sa_handler == SIG_IGN)
              ss->actions[sig].sa_handler = SIG_DFL;
          }
        __spin_unlock (&ss->lock);
        return 0;
 
       case INIT_TRACEMASK:
        _hurdsig_traced = value;
        return 0;
       }
     default:
       return EINVAL;
     }
 }
 
 kern_return_t
 _S_msg_set_init_int (mach_port_t msgport, mach_port_t auth,
                     int which, int value)
 {
   AUTHCHECK;
 
   return set_int (which, value);
 }
diff --git a/hurd/hurdsig.c b/hurd/hurdsig.c
index 75d60b0..6000731 100644
--- a/hurd/hurdsig.c
+++ b/hurd/hurdsig.c
@@ -17,138 +17,209 @@
    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
    02111-1307 USA.  */
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include <cthreads.h>          /* For `struct mutex'.  */
 #include <mach.h>
 #include <mach/thread_switch.h>
 
 #include <hurd.h>
 #include <hurd/id.h>
 #include <hurd/signal.h>
 
 #include "hurdfault.h"
 #include "hurdmalloc.h"                /* XXX */
 #include "../locale/localeinfo.h"
 
 const char *_hurdsig_getenv (const char *);
 
 struct mutex _hurd_siglock;
 int _hurd_stopped;
 
 /* Port that receives signals and other miscellaneous messages.  */
 mach_port_t _hurd_msgport;
 
 /* Thread listening on it.  */
 thread_t _hurd_msgport_thread;
 
-/* Thread which receives task-global signals.  */
-thread_t _hurd_sigthread;
-
 /* These are set up by _hurdsig_init.  */
 unsigned long int __hurd_sigthread_stack_base;
 unsigned long int __hurd_sigthread_stack_end;
 unsigned long int *__hurd_sigthread_variables;
 
 /* Linked-list of per-thread signal state.  */
 struct hurd_sigstate *_hurd_sigstates;
 
+/* Sigstate for the task-global signals.  */
+struct hurd_sigstate *_hurd_global_sigstate;
+
 /* Timeout for RPC's after interrupt_operation. */
 mach_msg_timeout_t _hurd_interrupted_rpc_timeout = 3000;
 
 static void
 default_sigaction (struct sigaction actions[NSIG])
 {
   int signo;
 
   __sigemptyset (&actions[0].sa_mask);
   actions[0].sa_flags = SA_RESTART;
   actions[0].sa_handler = SIG_DFL;
 
   for (signo = 1; signo < NSIG; ++signo)
     actions[signo] = actions[0];
 }
 
 struct hurd_sigstate *
 _hurd_thread_sigstate (thread_t thread)
 {
   struct hurd_sigstate *ss;
   __mutex_lock (&_hurd_siglock);
   for (ss = _hurd_sigstates; ss != NULL; ss = ss->next)
     if (ss->thread == thread)
        break;
   if (ss == NULL)
     {
       ss = malloc (sizeof (*ss));
       if (ss == NULL)
-       __libc_fatal ("hurd: Can't allocate thread sigstate\n");
+       __libc_fatal ("hurd: Can't allocate sigstate\n");
       ss->thread = thread;
       __spin_lock_init (&ss->lock);
 
       /* Initialize default state.  */
       __sigemptyset (&ss->blocked);
       __sigemptyset (&ss->pending);
       memset (&ss->sigaltstack, 0, sizeof (ss->sigaltstack));
       ss->preemptors = NULL;
       ss->suspended = MACH_PORT_NULL;
       ss->intr_port = MACH_PORT_NULL;
       ss->context = NULL;
 
-      /* Initialize the sigaction vector from the default signal receiving
-        thread's state, and its from the system defaults.  */
-      if (thread == _hurd_sigthread)
-       default_sigaction (ss->actions);
+      if (thread == MACH_PORT_NULL)
+       {
+         /* Process-wide sigstate, use the system defaults.  */
+         default_sigaction (ss->actions);
+       }
       else
        {
-         struct hurd_sigstate *s;
-         for (s = _hurd_sigstates; s != NULL; s = s->next)
-           if (s->thread == _hurd_sigthread)
-             break;
+         /* Use the global actions as a default for new threads.  */
+         struct hurd_sigstate *s = _hurd_global_sigstate;
          if (s)
            {
              __spin_lock (&s->lock);
              memcpy (ss->actions, s->actions, sizeof (s->actions));
              __spin_unlock (&s->lock);
            }
          else
            default_sigaction (ss->actions);
-       }
 
-      ss->next = _hurd_sigstates;
-      _hurd_sigstates = ss;
+         ss->next = _hurd_sigstates;
+         _hurd_sigstates = ss;
+       }
     }
   __mutex_unlock (&_hurd_siglock);
   return ss;
 }
+
+/* Make SS a global receiver, with pthread signal semantics.  */
+void
+_hurd_sigstate_set_global_rcv (struct hurd_sigstate *ss)
+{
+  assert (ss->thread != MACH_PORT_NULL);
+  ss->actions[0].sa_handler = SIG_IGN;
+}
+
+/* Check whether SS is a global receiver.  */
+static int
+sigstate_is_global_rcv (const struct hurd_sigstate *ss)
+{
+  return ss->actions[0].sa_handler == SIG_IGN;
+}
+
+/* Lock/unlock a hurd_sigstate structure.  If the accessors below require
+   it, the global sigstate will be locked as well.  */
+void
+_hurd_sigstate_lock (struct hurd_sigstate *ss)
+{
+  if (sigstate_is_global_rcv (ss))
+    __spin_lock (&_hurd_global_sigstate->lock);
+  __spin_lock (&ss->lock);
+}
+void
+_hurd_sigstate_unlock (struct hurd_sigstate *ss)
+{
+  __spin_unlock (&ss->lock);
+  if (sigstate_is_global_rcv (ss))
+    __spin_unlock (&_hurd_global_sigstate->lock);
+}
+
+/* Retreive a thread's full set of pending signals, including the global
+   ones if appropriate.  SS must be locked.  */
+sigset_t
+_hurd_sigstate_pending (const struct hurd_sigstate *ss)
+{
+  sigset_t pending = ss->pending;
+  if (sigstate_is_global_rcv (ss))
+    __sigorset (&pending, &pending, &_hurd_global_sigstate->pending);
+  return pending;
+}
+
+/* Clear a pending signal and return the associated detailed
+   signal information. SS must be locked, and must have signal SIGNO
+   pending, either directly or through the global sigstate.  */
+static struct hurd_signal_detail
+sigstate_clear_pending (struct hurd_sigstate *ss, int signo)
+{
+  if (sigstate_is_global_rcv (ss)
+      && __sigismember (&_hurd_global_sigstate->pending, signo))
+    {
+      __sigdelset (&_hurd_global_sigstate->pending, signo);
+      return _hurd_global_sigstate->pending_data[signo];
+    }
+
+  assert (__sigismember (&ss->pending, signo));
+  __sigdelset (&ss->pending, signo);
+  return ss->pending_data[signo];
+}
+
+/* Retreive a thread's action vector.  SS must be locked.  */
+struct sigaction *
+_hurd_sigstate_actions (struct hurd_sigstate *ss)
+{
+  if (sigstate_is_global_rcv (ss))
+    return _hurd_global_sigstate->actions;
+  else
+    return ss->actions;
+}
+
 
 /* Signal delivery itself is on this page.  */
 
 #include <hurd/fd.h>
 #include <hurd/crash.h>
 #include <hurd/resource.h>
 #include <hurd/paths.h>
 #include <setjmp.h>
 #include <fcntl.h>
 #include <sys/wait.h>
 #include <thread_state.h>
 #include <hurd/msg_server.h>
 #include <hurd/msg_reply.h>    /* For __msg_sig_post_reply.  */
 #include <hurd/interrupt.h>
 #include <assert.h>
 #include <unistd.h>
 
 
 /* Call the crash dump server to mummify us before we die.
    Returns nonzero if a core file was written.  */
 static int
 write_corefile (int signo, const struct hurd_signal_detail *detail)
 {
   error_t err;
   mach_port_t coreserver;
   file_t file, coredir;
   const char *name;
 
   /* Don't bother locking since we just read the one word.  */
   rlim_t corelimit = _hurd_rlimits[RLIMIT_CORE].rlim_cur;
@@ -189,60 +260,62 @@ write_corefile (int signo, const struct 
hurd_signal_detail *detail)
   /* Call the core dumping server to write the core file.  */
   err = __crash_dump_task (coreserver,
                           __mach_task_self (),
                           file,
                           signo, detail->code, detail->error,
                           detail->exc, detail->exc_code, detail->exc_subcode,
                           _hurd_ports[INIT_PORT_CTTYID].port,
                           MACH_MSG_TYPE_COPY_SEND);
   __mach_port_deallocate (__mach_task_self (), coreserver);
 
   if (! err && file != MACH_PORT_NULL)
     /* The core dump into FILE succeeded, so now link it into the
        directory.  */
     err = __dir_link (coredir, file, name, 1);
   __mach_port_deallocate (__mach_task_self (), file);
   __mach_port_deallocate (__mach_task_self (), coredir);
   return !err && file != MACH_PORT_NULL;
 }
 
 
 /* The lowest-numbered thread state flavor value is 1,
    so we use bit 0 in machine_thread_all_state.set to
    record whether we have done thread_abort.  */
 #define THREAD_ABORTED 1
 
 /* SS->thread is suspended.  Abort the thread and get its basic state.  */
 static void
 abort_thread (struct hurd_sigstate *ss, struct machine_thread_all_state *state,
              void (*reply) (void))
 {
+  assert (ss->thread != MACH_PORT_NULL);
+
   if (!(state->set & THREAD_ABORTED))
     {
       error_t err = __thread_abort (ss->thread);
       assert_perror (err);
       /* Clear all thread state flavor set bits, because thread_abort may
         have changed the state.  */
       state->set = THREAD_ABORTED;
     }
 
   if (reply)
     (*reply) ();
 
   machine_get_basic_state (ss->thread, state);
 }
 
 /* Find the location of the MiG reply port cell in use by the thread whose
    state is described by THREAD_STATE.  If SIGTHREAD is nonzero, make sure
    that this location can be set without faulting, or else return NULL.  */
 
 static mach_port_t *
 interrupted_reply_port_location (struct machine_thread_all_state *thread_state,
                                 int sigthread)
 {
   mach_port_t *portloc = (mach_port_t *) __hurd_threadvar_location_from_sp
     (_HURD_THREADVAR_MIG_REPLY, (void *) thread_state->basic.SP);
 
   if (sigthread && _hurdsig_catch_memory_fault (portloc))
     /* Faulted trying to read the stack.  */
     return NULL;
 
@@ -328,61 +401,61 @@ _hurdsig_abort_rpcs (struct hurd_sigstate *ss, int signo, 
int sigthread,
        mach_port_t *reply = interrupted_reply_port_location (state,
                                                              sigthread);
        error_t err = __interrupt_operation (intr_port, 
_hurdsig_interrupt_timeout);
 
        if (err)
          {
            if (reply)
              {
                /* The interrupt didn't work.
                   Destroy the receive right the thread is blocked on.  */
                __mach_port_destroy (__mach_task_self (), *reply);
                *reply = MACH_PORT_NULL;
              }
 
            /* The system call return value register now contains
               MACH_RCV_INTERRUPTED; when mach_msg resumes, it will retry the
               call.  Since we have just destroyed the receive right, the
               retry will fail with MACH_RCV_INVALID_NAME.  Instead, just
               change the return value here to EINTR so mach_msg will not
               retry and the EINTR error code will propagate up.  */
            state->basic.SYSRETURN = EINTR;
            *state_change = 1;
          }
        else if (reply)
          rcv_port = *reply;
 
        /* All threads whose RPCs were interrupted by the interrupt_operation
           call above will retry their RPCs unless we clear SS->intr_port.
           So we clear it for the thread taking a signal when SA_RESTART is
           clear, so that its call returns EINTR.  */
-       if (! signo || !(ss->actions[signo].sa_flags & SA_RESTART))
+       if (! signo || !(_hurd_sigstate_actions (ss) [signo].sa_flags & 
SA_RESTART))
          ss->intr_port = MACH_PORT_NULL;
       }
 
   return rcv_port;
 }
 
 
 /* Abort the RPCs being run by all threads but this one;
    all other threads should be suspended.  If LIVE is nonzero, those
    threads may run again, so they should be adjusted as necessary to be
    happy when resumed.  STATE is clobbered as a scratch area; its initial
    contents are ignored, and its contents on return are not useful.  */
 
 static void
 abort_all_rpcs (int signo, struct machine_thread_all_state *state, int live)
 {
   /* We can just loop over the sigstates.  Any thread doing something
      interruptible must have one.  We needn't bother locking because all
      other threads are stopped.  */
 
   struct hurd_sigstate *ss;
   size_t nthreads;
   mach_port_t *reply_ports;
 
   /* First loop over the sigstates to count them.
      We need to know how big a vector we will need for REPLY_PORTS.  */
   nthreads = 0;
   for (ss = _hurd_sigstates; ss != NULL; ss = ss->next)
     ++nthreads;
 
@@ -506,162 +579,165 @@ post_signal (struct hurd_sigstate *ss,
                 ({
                   /* Hold the siglock while stopping other threads to be
                      sure it is not held by another thread afterwards.  */
                   __mutex_lock (&_hurd_siglock);
                   __proc_dostop (port, _hurd_msgport_thread);
                   __mutex_unlock (&_hurd_siglock);
                   abort_all_rpcs (signo, &thread_state, 1);
                   reply ();
                   __proc_mark_stop (port, signo, detail->code);
                 }));
       _hurd_stopped = 1;
     }
   /* Resume the process after a suspension.  */
   void resume (void)
     {
       /* Resume the process from being stopped.  */
       thread_t *threads;
       mach_msg_type_number_t nthreads, i;
       error_t err;
 
       if (! _hurd_stopped)
        return;
 
       /* Tell the proc server we are continuing.  */
       __USEPORT (PROC, __proc_mark_cont (port));
       /* Fetch ports to all our threads and resume them.  */
       err = __task_threads (__mach_task_self (), &threads, &nthreads);
       assert_perror (err);
       for (i = 0; i < nthreads; ++i)
        {
-         if (threads[i] != _hurd_msgport_thread &&
-             (act != handle || threads[i] != ss->thread))
+         if (act == handle && threads[i] == ss->thread)
+           {
+             /* The thread that will run the handler is kept suspended.  */
+             ss_suspended = 1;
+           }
+         else if (threads[i] != _hurd_msgport_thread)
            {
              err = __thread_resume (threads[i]);
              assert_perror (err);
            }
          err = __mach_port_deallocate (__mach_task_self (),
                                        threads[i]);
          assert_perror (err);
        }
       __vm_deallocate (__mach_task_self (),
                       (vm_address_t) threads,
                       nthreads * sizeof *threads);
       _hurd_stopped = 0;
-      if (act == handle)
-       /* The thread that will run the handler is already suspended.  */
-       ss_suspended = 1;
     }
 
   error_t err;
   sighandler_t handler;
 
   if (signo == 0)
     {
       if (untraced)
        {
          /* This is PTRACE_CONTINUE.  */
          act = ignore;
          resume ();
        }
 
       /* This call is just to check for pending signals.  */
-      __spin_lock (&ss->lock);
+      _hurd_sigstate_lock (ss);
       return 1;
     }
 
   thread_state.set = 0;                /* We know nothing.  */
 
-  __spin_lock (&ss->lock);
+  _hurd_sigstate_lock (ss);
 
   /* Check for a preempted signal.  Preempted signals can arrive during
      critical sections.  */
+  /* XXX how does this mix with _hurd_global_sigstate?  Should its PREEMPTORS
+   * field replace _hurdsig_preemptors?  */
   {
     inline sighandler_t try_preemptor (struct hurd_signal_preemptor *pe)
       {                                /* PE cannot be null.  */
        do
          {
            if (HURD_PREEMPT_SIGNAL_P (pe, signo, detail->code))
              {
                if (pe->preemptor)
                  {
                    sighandler_t handler = (*pe->preemptor) (pe, ss,
                                                             &signo, detail);
                    if (handler != SIG_ERR)
                      return handler;
                  }
                else
                  return pe->handler;
              }
            pe = pe->next;
          } while (pe != 0);
        return SIG_ERR;
       }
 
     handler = ss->preemptors ? try_preemptor (ss->preemptors) : SIG_ERR;
 
     /* If no thread-specific preemptor, check for a global one.  */
     if (handler == SIG_ERR && __sigismember (&_hurdsig_preempted_set, signo))
       {
        __mutex_lock (&_hurd_siglock);
        handler = try_preemptor (_hurdsig_preemptors);
        __mutex_unlock (&_hurd_siglock);
       }
   }
 
   ss_suspended = 0;
 
   if (handler == SIG_IGN)
     /* Ignore the signal altogether.  */
     act = ignore;
   else if (handler != SIG_ERR)
     /* Run the preemption-provided handler.  */
     act = handle;
   else
     {
       /* No preemption.  Do normal handling.  */
 
       if (!untraced && __sigismember (&_hurdsig_traced, signo))
        {
          /* We are being traced.  Stop to tell the debugger of the signal.  */
          if (_hurd_stopped)
            /* Already stopped.  Mark the signal as pending;
               when resumed, we will notice it and stop again.  */
            mark_pending ();
          else
            suspend ();
-         __spin_unlock (&ss->lock);
+         _hurd_sigstate_unlock (ss);
          reply ();
          return 0;
        }
 
-      handler = ss->actions[signo].sa_handler;
+      handler = _hurd_sigstate_actions (ss) [signo].sa_handler;
 
       if (handler == SIG_DFL)
        /* Figure out the default action for this signal.  */
        switch (signo)
          {
          case 0:
            /* A sig_post msg with SIGNO==0 is sent to
               tell us to check for pending signals.  */
            act = ignore;
            break;
 
          case SIGTTIN:
          case SIGTTOU:
          case SIGSTOP:
          case SIGTSTP:
            act = stop;
            break;
 
          case SIGCONT:
          case SIGIO:
          case SIGURG:
          case SIGCHLD:
          case SIGWINCH:
            act = ignore;
            break;
 
          case SIGQUIT:
          case SIGILL:
          case SIGTRAP:
          case SIGIOT:
@@ -737,104 +813,107 @@ post_signal (struct hurd_sigstate *ss,
 
   /* Perform the chosen action for the signal.  */
   switch (act)
     {
     case stop:
       if (_hurd_stopped)
        {
          /* We are already stopped, but receiving an untraced stop
             signal.  Instead of resuming and suspending again, just
             notify the proc server of the new stop signal.  */
          error_t err = __USEPORT (PROC, __proc_mark_stop
                                   (port, signo, detail->code));
          assert_perror (err);
        }
       else
        /* Suspend the process.  */
        suspend ();
       break;
 
     case ignore:
       if (detail->exc)
        /* Blocking or ignoring a machine exception is fatal.
           Otherwise we could just spin on the faulting instruction.  */
        goto fatal;
 
       /* Nobody cares about this signal.  If there was a call to resume
         above in SIGCONT processing and we've left a thread suspended,
         now's the time to set it going. */
       if (ss_suspended)
        {
+         assert (ss->thread != MACH_PORT_NULL);
          err = __thread_resume (ss->thread);
          assert_perror (err);
          ss_suspended = 0;
        }
       break;
 
     sigbomb:
       /* We got a fault setting up the stack frame for the handler.
         Nothing to do but die; BSD gets SIGILL in this case.  */
       detail->code = signo;    /* XXX ? */
       signo = SIGILL;
 
     fatal:
       act = core;
       /* FALLTHROUGH */
 
     case term:                 /* Time to die.  */
     case core:                 /* And leave a rotting corpse.  */
       /* Have the proc server stop all other threads in our task.  */
       err = __USEPORT (PROC, __proc_dostop (port, _hurd_msgport_thread));
       assert_perror (err);
       /* No more user instructions will be executed.
         The signal can now be considered delivered.  */
       reply ();
       /* Abort all server operations now in progress.  */
       abort_all_rpcs (signo, &thread_state, 0);
 
       {
        int status = W_EXITCODE (0, signo);
        /* Do a core dump if desired.  Only set the wait status bit saying we
           in fact dumped core if the operation was actually successful.  */
        if (act == core && write_corefile (signo, detail))
          status |= WCOREFLAG;
        /* Tell proc how we died and then stick the saber in the gut.  */
        _hurd_exit (status);
        /* NOTREACHED */
       }
 
     case handle:
       /* Call a handler for this signal.  */
       {
        struct sigcontext *scp, ocontext;
        int wait_for_reply, state_changed;
 
+       assert (ss->thread != MACH_PORT_NULL);
+
        /* Stop the thread and abort its pending RPC operations.  */
        if (! ss_suspended)
          {
            err = __thread_suspend (ss->thread);
            assert_perror (err);
          }
 
        /* Abort the thread's kernel context, so any pending message send
           or receive completes immediately or aborts.  If an interruptible
           RPC is in progress, abort_rpcs will do this.  But we must always
           do it before fetching the thread's state, because
           thread_get_state is never kosher before thread_abort.  */
        abort_thread (ss, &thread_state, NULL);
 
        if (ss->context)
          {
            /* We have a previous sigcontext that sigreturn was about
               to restore when another signal arrived.  */
 
            mach_port_t *loc;
 
            if (_hurdsig_catch_memory_fault (ss->context))
              {
                /* We faulted reading the thread's stack.  Forget that
                   context and pretend it wasn't there.  It almost
                   certainly crash if this handler returns, but that's it's
                   problem.  */
                ss->context = NULL;
              }
            else
@@ -915,210 +994,212 @@ post_signal (struct hurd_sigstate *ss,
          /* Fetch the thread variable for the MiG reply port,
             and set it to MACH_PORT_NULL.  */
          mach_port_t *loc = interrupted_reply_port_location (&thread_state,
                                                              1);
          if (loc)
            {
              scp->sc_reply_port = *loc;
              *loc = MACH_PORT_NULL;
            }
          else
            scp->sc_reply_port = MACH_PORT_NULL;
 
          /* Save the intr_port in use by the interrupted code,
             and clear the cell before running the trampoline.  */
          scp->sc_intr_port = ss->intr_port;
          ss->intr_port = MACH_PORT_NULL;
 
          if (ss->context)
            {
              /* After the handler runs we will restore to the state in
                 SS->context, not the state of the thread now.  So restore
                 that context's reply port and intr port.  */
 
              scp->sc_reply_port = ss->context->sc_reply_port;
              scp->sc_intr_port = ss->context->sc_intr_port;
 
              ss->context = NULL;
            }
        }
 
+       struct sigaction *action = & _hurd_sigstate_actions (ss) [signo];
+
        /* Backdoor extra argument to signal handler.  */
        scp->sc_error = detail->error;
 
        /* Block requested signals while running the handler.  */
        scp->sc_mask = ss->blocked;
-       __sigorset (&ss->blocked, &ss->blocked, &ss->actions[signo].sa_mask);
+       __sigorset (&ss->blocked, &ss->blocked, &action->sa_mask);
 
        /* Also block SIGNO unless we're asked not to.  */
-       if (! (ss->actions[signo].sa_flags & (SA_RESETHAND | SA_NODEFER)))
+       if (! (action->sa_flags & (SA_RESETHAND | SA_NODEFER)))
          __sigaddset (&ss->blocked, signo);
 
        /* Reset to SIG_DFL if requested.  SIGILL and SIGTRAP cannot
            be automatically reset when delivered; the system silently
            enforces this restriction.  */
-       if (ss->actions[signo].sa_flags & SA_RESETHAND
+       if (action->sa_flags & SA_RESETHAND
            && signo != SIGILL && signo != SIGTRAP)
-         ss->actions[signo].sa_handler = SIG_DFL;
+         action->sa_handler = SIG_DFL;
 
        /* Any sigsuspend call must return after the handler does.  */
        wake_sigsuspend (ss);
 
        /* Start the thread running the handler (or possibly waiting for an
           RPC reply before running the handler).  */
        err = __thread_set_state (ss->thread, MACHINE_THREAD_STATE_FLAVOR,
                                  (natural_t *) &thread_state.basic,
                                  MACHINE_THREAD_STATE_COUNT);
        assert_perror (err);
        err = __thread_resume (ss->thread);
        assert_perror (err);
        thread_state.set = 0;   /* Everything we know is now wrong.  */
        break;
       }
     }
 
   return 1;
 }
 
 /* Return the set of pending signals in SS which should be delivered. */
 static sigset_t
 pending_signals (struct hurd_sigstate *ss)
 {
   /* We don't worry about any pending signals if we are stopped, nor if
      SS is in a critical section.  We are guaranteed to get a sig_post
      message before any of them become deliverable: either the SIGCONT
      signal, or a sig_post with SIGNO==0 as an explicit poll when the
      thread finishes its critical section.  */
   if (_hurd_stopped || __spin_lock_locked (&ss->critical_section_lock))
     return 0;
 
-  return ss->pending & ~ss->blocked;
+  return _hurd_sigstate_pending (ss) & ~ss->blocked;
 }
 
 /* Post the specified pending signals in SS and return 1.  If one of
    them is traced, abort immediately and return 0.  SS must be locked on
    entry and will be unlocked in all cases.  */
 static int
 post_pending (struct hurd_sigstate *ss, sigset_t pending, void (*reply) (void))
 {
   int signo;
   struct hurd_signal_detail detail;
 
   for (signo = 1; signo < NSIG; ++signo)
     if (__sigismember (&pending, signo))
       {
-       __sigdelset (&ss->pending, signo);
-       detail = ss->pending_data[signo];
-       __spin_unlock (&ss->lock);
+       detail = sigstate_clear_pending (ss, signo);
+       _hurd_sigstate_unlock (ss);
 
        /* Will reacquire the lock, except if the signal is traced.  */
        if (! post_signal (ss, signo, &detail, 0, reply))
          return 0;
       }
 
   /* No more signals pending; SS->lock is still locked.  */
-  __spin_unlock (&ss->lock);
+  _hurd_sigstate_unlock (ss);
 
   return 1;
 }
 
 /* Post all the pending signals of all threads and return 1.  If a traced
    signal is encountered, abort immediately and return 0.  */
 static int
 post_all_pending_signals (void (*reply) (void))
 {
   struct hurd_sigstate *ss;
   sigset_t pending;
 
   for (;;)
     {
       __mutex_lock (&_hurd_siglock);
       for (ss = _hurd_sigstates; ss != NULL; ss = ss->next)
         {
-         __spin_lock (&ss->lock);
+         _hurd_sigstate_lock (ss);
 
          pending = pending_signals (ss);
          if (pending)
            /* post_pending() below will unlock SS. */
            break;
 
-         __spin_unlock (&ss->lock);
+         _hurd_sigstate_unlock (ss);
        }
       __mutex_unlock (&_hurd_siglock);
 
       if (! pending)
        return 1;
       if (! post_pending (ss, pending, reply))
        return 0;
     }
 }
 
 /* Deliver a signal.  SS is not locked.  */
 void
 _hurd_internal_post_signal (struct hurd_sigstate *ss,
                            int signo, struct hurd_signal_detail *detail,
                            mach_port_t reply_port,
                            mach_msg_type_name_t reply_port_type,
                            int untraced)
 {
   /* Reply to this sig_post message.  */
   __typeof (__msg_sig_post_reply) *reply_rpc
     = (untraced ? __msg_sig_post_untraced_reply : __msg_sig_post_reply);
   void reply (void)
     {
       error_t err;
       if (reply_port == MACH_PORT_NULL)
        return;
       err = (*reply_rpc) (reply_port, reply_port_type, 0);
       reply_port = MACH_PORT_NULL;
       if (err != MACH_SEND_INVALID_DEST) /* Ignore dead reply port.  */
        assert_perror (err);
     }
 
   if (! post_signal (ss, signo, detail, untraced, reply))
     return;
 
   /* The signal was neither fatal nor traced.  We still hold SS->lock.  */
-  if (signo != 0)
+  if (signo != 0 && ss->thread != MACH_PORT_NULL)
     {
       /* The signal has either been ignored or is now being handled.  We can
         consider it delivered and reply to the killer.  */
       reply ();
 
       /* Post any pending signals for this thread.  */
       if (! post_pending (ss, pending_signals (ss), reply))
        return;
     }
   else
     {
-      /* We need to check for pending signals for all threads.  */
-      __spin_unlock (&ss->lock);
+      /* If this was a process-wide signal or a poll request, we need
+        to check for pending signals for all threads.  */
+      _hurd_sigstate_unlock (ss);
       if (! post_all_pending_signals (reply))
        return;
 
       /* All pending signals delivered to all threads.
         Now we can send the reply message even for signal 0.  */
       reply ();
     }
 }
 
 /* Decide whether REFPORT enables the sender to send us a SIGNO signal.
    Returns zero if so, otherwise the error code to return to the sender.  */
 
 static error_t
 signal_allowed (int signo, mach_port_t refport)
 {
   if (signo < 0 || signo >= NSIG)
     return EINVAL;
 
   if (refport == __mach_task_self ())
     /* Can send any signal.  */
     goto win;
 
   /* Avoid needing to check for this below.  */
   if (refport == MACH_PORT_NULL)
     return EPERM;
 
   switch (signo)
     {
     case SIGINT:
     case SIGQUIT:
@@ -1190,146 +1271,153 @@ signal_allowed (int signo, mach_port_t refport)
     }
 
   /* If this signal is legit, we have done `goto win' by now.
      When we return the error, mig deallocates REFPORT.  */
   return EPERM;
 
  win:
   /* Deallocate the REFPORT send right; we are done with it.  */
   __mach_port_deallocate (__mach_task_self (), refport);
 
   return 0;
 }
 
 /* Implement the sig_post RPC from <hurd/msg.defs>;
    sent when someone wants us to get a signal.  */
 kern_return_t
 _S_msg_sig_post (mach_port_t me,
                 mach_port_t reply_port, mach_msg_type_name_t reply_port_type,
                 int signo, natural_t sigcode,
                 mach_port_t refport)
 {
   error_t err;
   struct hurd_signal_detail d;
 
   if (err = signal_allowed (signo, refport))
     return err;
 
   d.code = sigcode;
   d.exc = 0;
 
-  /* Post the signal to the designated signal-receiving thread.  This will
+  /* Post the signal to XXX[the designated signal-receiving thread.]  This will
      reply when the signal can be considered delivered.  */
-  _hurd_internal_post_signal (_hurd_thread_sigstate (_hurd_sigthread),
+  _hurd_internal_post_signal (_hurd_global_sigstate,
                              signo, &d, reply_port, reply_port_type,
                              0); /* Stop if traced.  */
 
   return MIG_NO_REPLY;         /* Already replied.  */
 }
 
 /* Implement the sig_post_untraced RPC from <hurd/msg.defs>;
    sent when the debugger wants us to really get a signal
    even if we are traced.  */
 kern_return_t
 _S_msg_sig_post_untraced (mach_port_t me,
                          mach_port_t reply_port,
                          mach_msg_type_name_t reply_port_type,
                          int signo, natural_t sigcode,
                          mach_port_t refport)
 {
   error_t err;
   struct hurd_signal_detail d;
 
   if (err = signal_allowed (signo, refport))
     return err;
 
   d.code = sigcode;
   d.exc = 0;
 
   /* Post the signal to the designated signal-receiving thread.  This will
      reply when the signal can be considered delivered.  */
-  _hurd_internal_post_signal (_hurd_thread_sigstate (_hurd_sigthread),
+  _hurd_internal_post_signal (_hurd_global_sigstate,
                              signo, &d, reply_port, reply_port_type,
                              1); /* Untraced flag. */
 
   return MIG_NO_REPLY;         /* Already replied.  */
 }
 
 extern void __mig_init (void *);
 
 #include <mach/task_special_ports.h>
 
-/* Initialize the message port and _hurd_sigthread and start the signal
-   thread.  */
+/* Initialize the message port, _hurd_sigthread and _hurd_global_sigstate
+   and start the signal thread.  */
 
 void
 _hurdsig_init (const int *intarray, size_t intarraysize)
 {
   error_t err;
   vm_size_t stacksize;
   struct hurd_sigstate *ss;
 
   __mutex_init (&_hurd_siglock);
 
   err = __mach_port_allocate (__mach_task_self (),
                              MACH_PORT_RIGHT_RECEIVE,
                              &_hurd_msgport);
   assert_perror (err);
 
   /* Make a send right to the signal port.  */
   err = __mach_port_insert_right (__mach_task_self (),
                                  _hurd_msgport,
                                  _hurd_msgport,
                                  MACH_MSG_TYPE_MAKE_SEND);
   assert_perror (err);
 
+  /* Initialize the global signal state.  */
+  _hurd_global_sigstate = _hurd_thread_sigstate (MACH_PORT_NULL);
+
+  /* We block all signals, and let actual threads pull them from the
+     pending mask.  */
+  __sigfillset(& _hurd_global_sigstate->blocked);
+
   /* Initialize the main thread's signal state.  */
   ss = _hurd_self_sigstate ();
 
-  /* Copy inherited values from our parent (or pre-exec process state)
-     into the signal settings of the main thread.  */
+  /* Mark it as a process-wide signal receiver.  Threads in this set use
+     the common action vector in _hurd_global_sigstate.  */
+  _hurd_sigstate_set_global_rcv (ss);
+
+  /* Copy inherited signal settings from our parent (or pre-exec process
+     state) */
   if (intarraysize > INIT_SIGMASK)
     ss->blocked = intarray[INIT_SIGMASK];
   if (intarraysize > INIT_SIGPENDING)
-    ss->pending = intarray[INIT_SIGPENDING];
+    _hurd_global_sigstate->pending = intarray[INIT_SIGPENDING];
   if (intarraysize > INIT_SIGIGN && intarray[INIT_SIGIGN] != 0)
     {
       int signo;
       for (signo = 1; signo < NSIG; ++signo)
        if (intarray[INIT_SIGIGN] & __sigmask(signo))
-         ss->actions[signo].sa_handler = SIG_IGN;
+         _hurd_global_sigstate->actions[signo].sa_handler = SIG_IGN;
     }
 
-  /* Set the default thread to receive task-global signals
-     to this one, the main (first) user thread.  */
-  _hurd_sigthread = ss->thread;
-
   /* Start the signal thread listening on the message port.  */
 
   if (__hurd_threadvar_stack_mask == 0)
     {
       err = __thread_create (__mach_task_self (), &_hurd_msgport_thread);
       assert_perror (err);
 
       stacksize = __vm_page_size * 8; /* Small stack for signal thread.  */
       err = __mach_setup_thread (__mach_task_self (), _hurd_msgport_thread,
                                 _hurd_msgport_receive,
                                 (vm_address_t *) &__hurd_sigthread_stack_base,
                                 &stacksize);
       assert_perror (err);
       err = __mach_setup_tls (_hurd_msgport_thread);
       assert_perror (err);
 
       __hurd_sigthread_stack_end = __hurd_sigthread_stack_base + stacksize;
       __hurd_sigthread_variables =
        malloc (__hurd_threadvar_max * sizeof (unsigned long int));
       if (__hurd_sigthread_variables == NULL)
        __libc_fatal ("hurd: Can't allocate threadvars for signal thread\n");
       memset (__hurd_sigthread_variables, 0,
              __hurd_threadvar_max * sizeof (unsigned long int));
 
       /* Reinitialize the MiG support routines so they will use a per-thread
         variable for the cached reply port.  */
       __mig_init ((void *) __hurd_sigthread_stack_base);
 
       err = __thread_resume (_hurd_msgport_thread);
       assert_perror (err);
diff --git a/sysdeps/mach/hurd/fork.c b/sysdeps/mach/hurd/fork.c
index 47c4ca3..12dd022 100644
--- a/sysdeps/mach/hurd/fork.c
+++ b/sysdeps/mach/hurd/fork.c
@@ -432,60 +432,61 @@ __fork (void)
            }
        }
 
       /* Unlock the standard port cells.  The child must unlock its own
         copies too.  */
       for (i = 0; i < _hurd_nports; ++i)
        __spin_unlock (&_hurd_ports[i].lock);
       ports_locked = 0;
 
       /* All state has now been copied from the parent.  It is safe to
         resume other parent threads.  */
       resume_threads ();
 
       /* Create the child main user thread and signal thread.  */
       if ((err = __thread_create (newtask, &thread)) ||
          (err = __thread_create (newtask, &sigthread)))
        LOSE;
 
       /* Insert send rights for those threads.  We previously allocated
         dead name rights with the names we want to give the thread ports
         in the child as placeholders.  Now deallocate them so we can use
         the names.  */
       if ((err = __mach_port_deallocate (newtask, ss->thread)) ||
          (err = __mach_port_insert_right (newtask, ss->thread,
                                           thread, MACH_MSG_TYPE_COPY_SEND)))
        LOSE;
       /* We have one extra user reference created at the beginning of this
         function, accounted for by mach_port_names (and which will thus be
         accounted for in the child below).  This extra right gets consumed
         in the child by the store into _hurd_sigthread in the child fork.  */
+      /* XXX consumed? (_hurd_sigthread is no more) */
       if (thread_refs > 1 &&
          (err = __mach_port_mod_refs (newtask, ss->thread,
                                       MACH_PORT_RIGHT_SEND,
                                       thread_refs)))
        LOSE;
       if ((_hurd_msgport_thread != MACH_PORT_NULL) /* Let user have none.  */
          && ((err = __mach_port_deallocate (newtask, _hurd_msgport_thread)) ||
              (err = __mach_port_insert_right (newtask, _hurd_msgport_thread,
                                               sigthread,
                                               MACH_MSG_TYPE_COPY_SEND))))
        LOSE;
       if (sigthread_refs > 1 &&
          (err = __mach_port_mod_refs (newtask, _hurd_msgport_thread,
                                       MACH_PORT_RIGHT_SEND,
                                       sigthread_refs - 1)))
        LOSE;
 
       /* This seems like a convenient juncture to copy the proc server's
         idea of what addresses our argv and envp are found at from the
         parent into the child.  Since we happen to know that the child
         shares our memory image, it is we who should do this copying.  */
       {
        vm_address_t argv, envp;
        err = (__USEPORT (PROC, __proc_get_arg_locations (port, &argv, &envp))
               ?: __proc_set_arg_locations (newproc, argv, envp));
        if (err)
          LOSE;
       }
 
       /* Set the child signal thread up to run the msgport server function
@@ -594,115 +595,113 @@ __fork (void)
       if (porttypes)
        __vm_deallocate (__mach_task_self (),
                         (vm_address_t) porttypes,
                         nporttypes * sizeof (*porttypes));
       if (threads)
        {
          for (i = 0; i < nthreads; ++i)
            __mach_port_deallocate (__mach_task_self (), threads[i]);
          __vm_deallocate (__mach_task_self (),
                           (vm_address_t) threads,
                           nthreads * sizeof (*threads));
        }
 
       /* Run things that want to run in the parent to restore it to
         normality.  Usually prepare hooks and parent hooks are
         symmetrical: the prepare hook arrests state in some way for the
         fork, and the parent hook restores the state for the parent to
         continue executing normally.  */
       RUN_HOOK (_hurd_fork_parent_hook, ());
     }
   else
     {
       struct hurd_sigstate *oldstates;
 
       /* We are the child task.  Unlock the standard port cells, which were
         locked in the parent when we copied its memory.  The parent has
         inserted send rights with the names that were in the cells then.  */
       for (i = 0; i < _hurd_nports; ++i)
        __spin_unlock (&_hurd_ports[i].lock);
 
-      /* We are one of the (exactly) two threads in this new task, we
-        will take the task-global signals.  */
-      _hurd_sigthread = ss->thread;
-
       /* Claim our sigstate structure and unchain the rest: the
         threads existed in the parent task but don't exist in this
         task (the child process).  Delay freeing them until later
         because some of the further setup and unlocking might be
         required for free to work.  Before we finish cleaning up,
         we will reclaim the signal thread's sigstate structure (if
         it had one).  */
       oldstates = _hurd_sigstates;
       if (oldstates == ss)
        oldstates = ss->next;
       else
        {
          while (_hurd_sigstates->next != ss)
            _hurd_sigstates = _hurd_sigstates->next;
          _hurd_sigstates->next = ss->next;
        }
       ss->next = NULL;
       _hurd_sigstates = ss;
       __mutex_unlock (&_hurd_siglock);
 
+      /* We are one of the (exactly) two threads in this new task, we
+        will take the task-global signals.  */
+      _hurd_sigstate_set_global_rcv (ss);
+
       /* Fetch our new process IDs from the proc server.  No need to
         refetch our pgrp; it is always inherited from the parent (so
         _hurd_pgrp is already correct), and the proc server will send us a
         proc_newids notification when it changes.  */
       err = __USEPORT (PROC, __proc_getpids (port, &_hurd_pid, &_hurd_ppid,
                                             &_hurd_orphaned));
 
       /* Forking clears the trace flag.  */
       __sigemptyset (&_hurdsig_traced);
 
       /* Run things that want to run in the child task to set up.  */
       RUN_HOOK (_hurd_fork_child_hook, ());
 
       /* Set up proc server-assisted fault recovery for the signal thread.  */
       _hurdsig_fault_init ();
 
       /* Start the signal thread listening on the message port.  */
       if (!err)
        err = __thread_resume (_hurd_msgport_thread);
 
-      /* Reclaim the signal thread's sigstate structure and free the
-        other old sigstate structures.  */
+      /* Reclaim the signal thread's and global sigstate structures and
+         free the other old sigstate structures.  */
       while (oldstates != NULL)
        {
          struct hurd_sigstate *next = oldstates->next;
 
-         if (oldstates->thread == _hurd_msgport_thread)
+         if (oldstates->thread == _hurd_msgport_thread
+             || oldstates->thread == MACH_PORT_NULL)
            {
-             /* If we have a second signal state structure then we
-                must have been through here before--not good.  */
-             assert (_hurd_sigstates->next == 0);
              _hurd_sigstates->next = oldstates;
              oldstates->next = 0;
            }
          else
            free (oldstates);
 
          oldstates = next;
        }
 
       /* XXX what to do if we have any errors here? */
 
       pid = 0;
     }
 
   /* Unlock things we locked before creating the child task.
      They are locked in both the parent and child tasks.  */
   {
     void *const *p;
     for (p = symbol_set_first_element (_hurd_fork_locks);
         ! symbol_set_end_p (_hurd_fork_locks, p);
         ++p)
       __mutex_unlock (*p);
   }
 
   _hurd_critical_section_unlock (ss);
 
   return err ? __hurd_fail (err) : pid;
 }
 libc_hidden_def (__fork)
 
diff --git a/sysdeps/mach/hurd/sigaction.c b/sysdeps/mach/hurd/sigaction.c
index fe452e8..c682f39 100644
--- a/sysdeps/mach/hurd/sigaction.c
+++ b/sysdeps/mach/hurd/sigaction.c
@@ -24,73 +24,73 @@
 #include <hurd/signal.h>
 
 /* If ACT is not NULL, change the action for SIG to *ACT.
    If OACT is not NULL, put the old action for SIG in *OACT.  */
 int
 __sigaction (sig, act, oact)
      int sig;
      const struct sigaction *act;
      struct sigaction *oact;
 {
   struct hurd_sigstate *ss;
   struct sigaction a, old;
   sigset_t pending;
 
   if (sig <= 0 || sig >= NSIG ||
       (act != NULL && act->sa_handler != SIG_DFL &&
        ((__sigmask (sig) & _SIG_CANT_MASK) ||
        act->sa_handler == SIG_ERR)))
     {
       errno = EINVAL;
       return -1;
     }
 
   /* Copy so we fault before taking locks.  */
   if (act != NULL)
     a = *act;
 
   ss = _hurd_self_sigstate ();
 
   __spin_lock (&ss->critical_section_lock);
-  __spin_lock (&ss->lock);
-  old = ss->actions[sig];
+  _hurd_sigstate_lock (ss);
+  old = _hurd_sigstate_actions (ss) [sig];
   if (act != NULL)
-    ss->actions[sig] = a;
+    _hurd_sigstate_actions (ss) [sig] = a;
 
   if (act != NULL && sig == SIGCHLD &&
       (a.sa_flags & SA_NOCLDSTOP) != (old.sa_flags & SA_NOCLDSTOP))
     {
-      __spin_unlock (&ss->lock);
+      _hurd_sigstate_unlock (ss);
 
       /* Inform the proc server whether or not it should send us SIGCHLD for
         stopped children.  We do this in a critical section so that no
         SIGCHLD can arrive in the middle and be of indeterminate status.  */
       __USEPORT (PROC,
                 __proc_mod_stopchild (port, !(a.sa_flags & SA_NOCLDSTOP)));
 
-      __spin_lock (&ss->lock);
-      pending = ss->pending & ~ss->blocked;
+      _hurd_sigstate_lock (ss);
+      pending = _hurd_sigstate_pending (ss) & ~ss->blocked;
     }
   else if (act != NULL && (a.sa_handler == SIG_IGN || a.sa_handler == SIG_DFL))
     /* We are changing to an action that might be to ignore SIG signals.
        If SIG is blocked and pending and the new action is to ignore it, we
        must remove it from the pending set now; if the action is changed
        back and then SIG is unblocked, the signal pending now should not
        arrive.  So wake up the signal thread to check the new state and do
        the right thing.  */
-    pending = ss->pending & __sigmask (sig);
+    pending = _hurd_sigstate_pending (ss) & __sigmask (sig);
   else
     pending = 0;
 
-  __spin_unlock (&ss->lock);
+  _hurd_sigstate_unlock (ss);
   __spin_unlock (&ss->critical_section_lock);
 
   if (pending)
     __msg_sig_post (_hurd_msgport, 0, 0, __mach_task_self ());
 
   if (oact != NULL)
     *oact = old;
 
   return 0;
 }
 libc_hidden_def (__sigaction)
 weak_alias (__sigaction, sigaction)
diff --git a/sysdeps/mach/hurd/sigpending.c b/sysdeps/mach/hurd/sigpending.c
index 84ac927..8844c5b 100644
--- a/sysdeps/mach/hurd/sigpending.c
+++ b/sysdeps/mach/hurd/sigpending.c
@@ -11,37 +11,37 @@
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.
 
    You should have received a copy of the GNU Lesser General Public
    License along with the GNU C Library; if not, write to the Free
    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
    02111-1307 USA.  */
 
 #include <errno.h>
 #include <stddef.h>
 #include <hurd.h>
 #include <hurd/signal.h>
 
 
 /* Store in SET all signals that are blocked and pending.  */
 /* XXX should be __sigpending ? */
 int
 sigpending (set)
      sigset_t *set;
 {
   struct hurd_sigstate *ss;
   sigset_t pending;
 
   if (set == NULL)
     {
       errno = EINVAL;
       return -1;
     }
 
   ss = _hurd_self_sigstate ();
-  __spin_lock (&ss->lock);
-  pending = ss->pending;
-  __spin_unlock (&ss->lock);
+  _hurd_sigstate_lock (ss);
+  pending = _hurd_sigstate_pending (ss);
+  _hurd_sigstate_unlock (ss);
 
   *set = pending;
   return 0;
 }
diff --git a/sysdeps/mach/hurd/sigprocmask.c b/sysdeps/mach/hurd/sigprocmask.c
index cbb5ecc..0609dff 100644
--- a/sysdeps/mach/hurd/sigprocmask.c
+++ b/sysdeps/mach/hurd/sigprocmask.c
@@ -13,75 +13,75 @@
 
    You should have received a copy of the GNU Lesser General Public
    License along with the GNU C Library; if not, write to the Free
    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
    02111-1307 USA.  */
 
 #include <errno.h>
 #include <signal.h>
 #include <hurd.h>
 #include <hurd/signal.h>
 #include <hurd/msg.h>
 
 /* If SET is not NULL, modify the current set of blocked signals
    according to HOW, which may be SIG_BLOCK, SIG_UNBLOCK or SIG_SETMASK.
    If OSET is not NULL, store the old set of blocked signals in *OSET.  */
 int
 __sigprocmask (how, set, oset)
      int how;
      const sigset_t *set;
      sigset_t *oset;
 {
   struct hurd_sigstate *ss;
   sigset_t old, new;
   sigset_t pending;
 
   if (set != NULL)
     new = *set;
 
   ss = _hurd_self_sigstate ();
 
-  __spin_lock (&ss->lock);
+  _hurd_sigstate_lock (ss);
 
   old = ss->blocked;
 
   if (set != NULL)
     {
       switch (how)
        {
        case SIG_BLOCK:
          __sigorset (&ss->blocked, &ss->blocked, &new);
          break;
 
        case SIG_UNBLOCK:
          ss->blocked &= ~new;
          break;
 
        case SIG_SETMASK:
          ss->blocked = new;
          break;
 
        default:
-         __spin_unlock (&ss->lock);
+         _hurd_sigstate_unlock (ss);
          errno = EINVAL;
          return -1;
        }
 
       ss->blocked &= ~_SIG_CANT_MASK;
     }
 
-  pending = ss->pending & ~ss->blocked;
+  pending = _hurd_sigstate_pending (ss) & ~ss->blocked;
 
-  __spin_unlock (&ss->lock);
+  _hurd_sigstate_unlock (ss);
 
   if (oset != NULL)
     *oset = old;
 
   if (pending)
     /* Send a message to the signal thread so it
        will wake up and check for pending signals.  */
     __msg_sig_post (_hurd_msgport, 0, 0, __mach_task_self ());
 
   return 0;
 }
 
 weak_alias (__sigprocmask, sigprocmask)
diff --git a/sysdeps/mach/hurd/sigsuspend.c b/sysdeps/mach/hurd/sigsuspend.c
index 7e32472..4f0679a 100644
--- a/sysdeps/mach/hurd/sigsuspend.c
+++ b/sysdeps/mach/hurd/sigsuspend.c
@@ -16,71 +16,72 @@
    You should have received a copy of the GNU Lesser General Public
    License along with the GNU C Library; if not, write to the Free
    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
    02111-1307 USA.  */
 
 #include <errno.h>
 #include <hurd.h>
 #include <hurd/signal.h>
 #include <hurd/msg.h>
 
 /* Change the set of blocked signals to SET,
    wait until a signal arrives, and restore the set of blocked signals.  */
 int
 __sigsuspend (set)
      const sigset_t *set;
 {
   struct hurd_sigstate *ss;
   sigset_t newmask, oldmask, pending;
   mach_port_t wait;
   mach_msg_header_t msg;
 
   if (set != NULL)
     /* Crash before locking.  */
     newmask = *set;
 
   /* Get a fresh port we will wait on.  */
   wait = __mach_reply_port ();
 
   ss = _hurd_self_sigstate ();
 
-  __spin_lock (&ss->lock);
+  _hurd_sigstate_lock (ss);
 
   oldmask = ss->blocked;
   if (set != NULL)
     /* Change to the new blocked signal mask.  */
     ss->blocked = newmask & ~_SIG_CANT_MASK;
 
   /* Notice if any pending signals just became unblocked.  */
-  pending = ss->pending & ~ss->blocked;
+  pending = _hurd_sigstate_pending (ss) & ~ss->blocked;
 
   /* Tell the signal thread to message us when a signal arrives.  */
   ss->suspended = wait;
-  __spin_unlock (&ss->lock);
+  _hurd_sigstate_unlock (ss);
 
   if (pending)
     /* Tell the signal thread to check for pending signals.  */
     __msg_sig_post (_hurd_msgport, 0, 0, __mach_task_self ());
 
   /* Wait for the signal thread's message.  */
   __mach_msg (&msg, MACH_RCV_MSG, 0, sizeof (msg), wait,
              MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
   __mach_port_destroy (__mach_task_self (), wait);
 
-  __spin_lock (&ss->lock);
-  ss->blocked = oldmask;       /* Restore the old mask.  */
-  pending = ss->pending & ~ss->blocked;        /* Again check for pending 
signals.  */
-  __spin_unlock (&ss->lock);
+  /* Restore the old mask and check for pending signals again.  */
+  _hurd_sigstate_lock (ss);
+  ss->blocked = oldmask;
+  pending = _hurd_sigstate_pending(ss) & ~ss->blocked;
+  _hurd_sigstate_unlock (ss);
 
   if (pending)
     /* Tell the signal thread to check for pending signals.  */
     __msg_sig_post (_hurd_msgport, 0, 0, __mach_task_self ());
 
   /* We've been interrupted!  And a good thing, too.
      Otherwise we'd never return.
      That's right; this function always returns an error.  */
   errno = EINTR;
   return -1;
 }
 libc_hidden_def (__sigsuspend)
 strong_alias (__sigsuspend, sigsuspend_not_cancel)
 weak_alias (__sigsuspend, sigsuspend)
diff --git a/sysdeps/mach/hurd/sigwait.c b/sysdeps/mach/hurd/sigwait.c
index 9794076..591a61b 100644
--- a/sysdeps/mach/hurd/sigwait.c
+++ b/sysdeps/mach/hurd/sigwait.c
@@ -45,89 +45,90 @@ __sigwait (const sigset_t *set, int *sig)
        /* We've already been run; don't interfere. */
        return SIG_ERR;
 
       signo = *sigp;
 
       /* Make sure this is all kosher */
       assert (__sigismember (&mask, signo));
 
       /* Make sure this signal is unblocked */
       __sigdelset (&ss->blocked, signo);
 
       return pe->handler;
     }
 
   void
     handler (int sig)
     {
       assert (sig == signo);
       longjmp (buf, 1);
     }
 
   wait = __mach_reply_port ();
 
   if (set != NULL)
     /* Crash before locking */
     mask = *set;
   else
     __sigemptyset (&mask);
 
   ss = _hurd_self_sigstate ();
-  __spin_lock (&ss->lock);
+  _hurd_sigstate_lock (ss);
 
   /* See if one of these signals is currently pending.  */
-  __sigandset (&ready, &ss->pending, &mask);
+  sigset_t pending = _hurd_sigstate_pending (ss);
+  __sigandset (&ready, &pending, &mask);
   if (! __sigisemptyset (&ready))
     {
       for (signo = 1; signo < NSIG; signo++)
        if (__sigismember (&ready, signo))
          {
            __sigdelset (&ready, signo);
            goto all_done;
          }
       /* Huh?  Where'd it go? */
       abort ();
     }
 
   /* Wait for one of them to show up.  */
 
   if (!setjmp (buf))
     {
       /* Make the preemptor */
       preemptor.signals = mask;
       preemptor.first = 0;
       preemptor.last = -1;
       preemptor.preemptor = preempt_fun;
       preemptor.handler = handler;
 
       /* Install this preemptor */
       preemptor.next = ss->preemptors;
       ss->preemptors = &preemptor;
 
-      __spin_unlock (&ss->lock);
+      _hurd_sigstate_unlock (ss);
 
       /* Wait. */
       __mach_msg (&msg, MACH_RCV_MSG, 0, sizeof (msg), wait,
                  MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
       abort ();
     }
   else
     {
       assert (signo);
 
-      __spin_lock (&ss->lock);
+      _hurd_sigstate_lock (ss);
 
       /* Delete our preemptor. */
       assert (ss->preemptors == &preemptor);
       ss->preemptors = preemptor.next;
     }
 
 
 all_done:
-  spin_unlock (&ss->lock);
+  _hurd_sigstate_unlock (ss);
 
   __mach_port_destroy (__mach_task_self (), wait);
   *sig = signo;
   return 0;
 }
 
 weak_alias (__sigwait, sigwait)
diff --git a/sysdeps/mach/hurd/spawni.c b/sysdeps/mach/hurd/spawni.c
index 244ca2d..ddaaa65 100644
--- a/sysdeps/mach/hurd/spawni.c
+++ b/sysdeps/mach/hurd/spawni.c
@@ -212,80 +212,83 @@ __spawni (pid_t *pid, const char *file,
      for comments about the sequencing of these proc operations.  */
 
   err = __task_create (__mach_task_self (),
 #ifdef KERN_INVALID_LEDGER
                       NULL, 0, /* OSF Mach */
 #endif
                       0, &task);
   if (err)
     return __hurd_fail (err);
   // From here down we must deallocate TASK and PROC before returning.
   proc = MACH_PORT_NULL;
   auth = MACH_PORT_NULL;
   err = __USEPORT (PROC, __proc_task2pid (port, task, &new_pid));
   if (!err)
     err = __USEPORT (PROC, __proc_task2proc (port, task, &proc));
   if (!err)
     err = __USEPORT (PROC, __proc_child (port, task));
   if (err)
     goto out;
 
   /* Load up the ints to give the new program.  */
   memset (ints, 0, sizeof ints);
   ints[INIT_UMASK] = _hurd_umask;
   ints[INIT_TRACEMASK] = _hurdsig_traced;
 
   ss = _hurd_self_sigstate ();
 
   assert (! __spin_lock_locked (&ss->critical_section_lock));
   __spin_lock (&ss->critical_section_lock);
 
-  __spin_lock (&ss->lock);
+  _hurd_sigstate_lock (ss);
   ints[INIT_SIGMASK] = ss->blocked;
-  ints[INIT_SIGPENDING] = ss->pending;
+  ints[INIT_SIGPENDING] = _hurd_sigstate_pending (ss); /* XXX really? */
   ints[INIT_SIGIGN] = 0;
   /* Unless we were asked to reset all handlers to SIG_DFL,
      pass down the set of signals that were set to SIG_IGN.  */
-  if ((flags & POSIX_SPAWN_SETSIGDEF) == 0)
-    for (i = 1; i < NSIG; ++i)
-      if (ss->actions[i].sa_handler == SIG_IGN)
-       ints[INIT_SIGIGN] |= __sigmask (i);
+  {
+    struct sigaction *actions = _hurd_sigstate_actions (ss);
+    if ((flags & POSIX_SPAWN_SETSIGDEF) == 0)
+      for (i = 1; i < NSIG; ++i)
+       if (actions[i].sa_handler == SIG_IGN)
+         ints[INIT_SIGIGN] |= __sigmask (i);
+  }
 
-  /* We hold the sigstate lock until the exec has failed so that no signal
-     can arrive between when we pack the blocked and ignored signals, and
-     when the exec actually happens.  A signal handler could change what
+  /* We hold the critical section lock until the exec has failed so that no
+     signal can arrive between when we pack the blocked and ignored signals,
+     and when the exec actually happens.  A signal handler could change what
      signals are blocked and ignored.  Either the change will be reflected
      in the exec, or the signal will never be delivered.  Setting the
      critical section flag avoids anything we call trying to acquire the
      sigstate lock.  */
 
-  __spin_unlock (&ss->lock);
+  _hurd_sigstate_unlock (ss);
 
   /* Set signal mask.  */
   if ((flags & POSIX_SPAWN_SETSIGMASK) != 0)
     ints[INIT_SIGMASK] = attrp->__ss;
 
 #ifdef _POSIX_PRIORITY_SCHEDULING
   /* Set the scheduling algorithm and parameters.  */
 # error implement me
   if ((flags & (POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER))
       == POSIX_SPAWN_SETSCHEDPARAM)
     {
       if (__sched_setparam (0, &attrp->__sp) == -1)
        _exit (SPAWN_ERROR);
     }
   else if ((flags & POSIX_SPAWN_SETSCHEDULER) != 0)
     {
       if (__sched_setscheduler (0, attrp->__policy,
                                (flags & POSIX_SPAWN_SETSCHEDPARAM) != 0
                                ? &attrp->__sp : NULL) == -1)
        _exit (SPAWN_ERROR);
     }
 #endif
 
   /* Set the process group ID.  */
   if (!err && (flags & POSIX_SPAWN_SETPGROUP) != 0)
     err = __proc_setpgrp (proc, new_pid, attrp->__pgrp);
 
   /* Set the effective user and group IDs.  */
   if (!err && (flags & POSIX_SPAWN_RESETIDS) != 0)
     {
-- 
1.7.1




reply via email to

[Prev in Thread] Current Thread [Next in Thread]