New file.
[kopensolaris-gnu/glibc.git] / hurd / hurd / signal.h
index aace9e0..1973bcd 100644 (file)
@@ -1,5 +1,5 @@
 /* Implementing POSIX.1 signals under the Hurd.
-Copyright (C) 1993, 1994 Free Software Foundation, Inc.
+Copyright (C) 1993, 1994, 1995, 1996 Free Software Foundation, Inc.
 This file is part of the GNU C Library.
 
 The GNU C Library is free software; you can redistribute it and/or
@@ -35,46 +35,51 @@ Cambridge, MA 02139, USA.  */
 #include <hurd/hurd_types.h>
 #include <signal.h>
 #include <errno.h>
+#include <hurd/msg.h>
 
 #include <cthreads.h>          /* For `struct mutex'.  */
-#include <lock-intern.h>
+#include <spin-lock.h>
 #include <hurd/threadvar.h>    /* We cache sigstate in a threadvar.  */
+struct hurd_signal_preempter;  /* <hurd/sigpreempt.h> */
 
 
 /* Per-thread signal state.  */
 
 struct hurd_sigstate
   {
-    /* This mutex locks most of the rest of this structure.  It also acts
-       as a critical section lock for the thread (see below).  */
-    struct mutex lock;
-    int critical_section;
+    spin_lock_t lock;          /* Locks most of the rest of the structure.  */
+
+    int critical_section;      /* Nonzero if in critical section.  */
 
-    /* XXX This should perhaps instead be something that identifies
-       cthreads multiplexed on a single kernel thread.  */
     thread_t thread;
     struct hurd_sigstate *next; /* Linked-list of thread sigstates.  */
 
-    sigset_t blocked;
-    sigset_t pending;
+    sigset_t blocked;          /* What signals are blocked.  */
+    sigset_t pending;          /* Pending signals, possibly blocked.  */
     struct sigaction actions[NSIG];
     struct sigaltstack sigaltstack;
+
+    /* Chain of thread-local signal preempters; 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_preempter *preempters;
+
     struct
       {
        /* For each signal that may be pending, the
           sigcode and error code to deliver it with.  */
-       int code, error;
+       long int code;
+       error_t error;
       } pending_data[NSIG];
 
     /* If `suspended' is set when this thread gets a signal,
-       the signal thread clears it and then signals `arrived'.  */
-    int suspended;
-#ifdef noteven
-    struct condition arrived;
-#endif
+       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.  */
 
-    /* Not locked.  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
@@ -82,6 +87,15 @@ struct hurd_sigstate
        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.  */
@@ -94,45 +108,49 @@ extern struct mutex _hurd_siglock; /* Locks _hurd_sigstates.  */
 
 extern struct hurd_sigstate *_hurd_thread_sigstate (thread_t);
 
-/* Get the sigstate of the current thread, taking its lock.
+/* Get the sigstate of the current thread.
    This uses a per-thread variable to optimize the lookup.  */
 
-_EXTERN_INLINE struct hurd_sigstate *
-_hurd_self_sigstate_unlocked (void)
-{
-  struct hurd_sigstate **location =
-    (void *) __hurd_threadvar_location (_HURD_THREADVAR_SIGSTATE);
-  if (! *location)
-    {
-      *location = _hurd_thread_sigstate (__mach_thread_self ());
-      __mutex_unlock (&(*location)->lock);
-    }
-  return *location;
-}
+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__));
 
 _EXTERN_INLINE struct hurd_sigstate *
 _hurd_self_sigstate (void)
 {
   struct hurd_sigstate **location =
     (void *) __hurd_threadvar_location (_HURD_THREADVAR_SIGSTATE);
-  if (*location)
-    __mutex_lock (&(*location)->lock);
-  else
+  if (*location == NULL)
     *location = _hurd_thread_sigstate (__mach_thread_self ());
   return *location;
 }
 \f
+/* 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;
+\f
 /* 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.  Before entering a critical section, a thread must make
-   sure it holds its own sigstate lock.  The thread sets the
-   `critical_section' flag (which is not itself locked) to indicate the
-   lock is already held by the same thread.  Subroutines which contain
-   critical sections of their own then test this flag; if it is set, they
-   don't try to acquire the sigstate lock again, to avoid deadlock.  */
+   deadlock result.  */
 
 _EXTERN_INLINE void *
 _hurd_critical_section_lock (void)
@@ -147,18 +165,19 @@ _hurd_critical_section_lock (void)
        way; this locks the sigstate lock.  */
     ss = *location = _hurd_thread_sigstate (__mach_thread_self ());
   else
+    __spin_lock (&ss->lock);
+
+  if (ss->critical_section)
     {
-      if (ss->critical_section)
-       /* Some caller higher up has already acquired the critical section
-          lock.  We need do nothing.  The null pointer we return will
-          make _hurd_critical_section_unlock (below) be a no-op.  */
-       return NULL;
-      /* Acquire the sigstate lock to prevent any signal from arriving.  */
-      __mutex_lock (&ss->lock);
+      /* We are already in a critical section, so do nothing.  */
+      __spin_unlock (&ss->lock);
+      return NULL;
     }
-  /* Set the critical section flag so no later call will try to
-     take the sigstate lock while we already have it locked.  */
+
+  /* Set the critical section flag so no signal handler will run.  */
   ss->critical_section = 1;
+  __spin_unlock (&ss->lock);
+
   /* Return our sigstate pointer; this will be passed to
      _hurd_critical_section_unlock to clear the critical section flag. */
   return ss;
@@ -173,10 +192,18 @@ _hurd_critical_section_unlock (void *our_lock)
   else
     {
       /* It was us who acquired the critical section lock.  Clear the
-        critical section flag and unlock the sigstate lock.  */
+        critical section flag.  */
       struct hurd_sigstate *ss = our_lock;
+      sigset_t pending;
+      __spin_lock (&ss->lock);
       ss->critical_section = 0;
-      __mutex_unlock (&ss->lock);
+      pending = ss->pending & ~ss->blocked;
+      __spin_unlock (&ss->lock);
+      if (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, __mach_task_self ());
     }
 }
 
@@ -188,24 +215,6 @@ _hurd_critical_section_unlock (void *our_lock)
 #define HURD_CRITICAL_END \
       _hurd_critical_section_unlock (__hurd_critical__); } while (0)
 \f
-/* 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;
-\f
 /* Initialize the signal code, and start the signal thread.  */
 
 extern void _hurdsig_init (void);
@@ -219,23 +228,25 @@ extern void _hurdsig_fault_init (void);
    instead affects the calling thread.  */
 
 extern void _hurd_raise_signal (struct hurd_sigstate *ss,
-                               int signo, int sigcode, int sigerror);
+                               int signo, long int sigcode, int sigerror);
 
 /* Translate a Mach exception into a signal (machine-dependent).  */
 
 extern void _hurd_exception2signal (int exception, int code, int subcode,
-                                   int *signo, int *sigcode, int *error);
+                                   int *signo, long int *sigcode, int *error);
 
 
 /* Make the thread described by SS take the signal described by SIGNO and
-   SIGCODE.  When the signal can be considered delivered, sends a sig_post
-   reply message on REPLY_PORT indicating success.  SS->lock is held on
-   entry, and released before return.  */
+   SIGCODE.  If the process is traced, this will in fact stop with a SIGNO
+   as the stop signal unless UNTRACED is nonzero.  When the signal can be
+   considered delivered, sends a sig_post reply message on REPLY_PORT
+   indicating success.  SS is not locked.  */
 
 extern void _hurd_internal_post_signal (struct hurd_sigstate *ss,
-                                       int signo, int sigcode, int error,
+                                       int signo, long int sigcode, int error,
                                        mach_port_t reply_port,
-                                       mach_msg_type_name_t reply_port_type);
+                                       mach_msg_type_name_t reply_port_type,
+                                       int untraced);
 
 /* Set up STATE and SS to handle signal SIGNO by running HANDLER.  If
    RPC_WAIT is nonzero, the thread needs to wait for a pending RPC to
@@ -247,27 +258,21 @@ extern void _hurd_internal_post_signal (struct hurd_sigstate *ss,
 struct machine_thread_all_state;
 extern struct sigcontext *
 _hurd_setup_sighandler (struct hurd_sigstate *ss, __sighandler_t handler,
-                       int signo, int sigcode,
+                       int signo, long int sigcode,
                        int rpc_wait, struct machine_thread_all_state *state);
 
 /* Function run by the signal thread to receive from the signal port.  */
 
 extern void _hurd_msgport_receive (void);
 
-/* STATE describes a thread that had intr_port set (meaning it was inside
-   HURD_EINTR_RPC), after it has been thread_abort'd.  It it looks to have
-   just completed a mach_msg_trap system call that returned
-   MACH_RCV_INTERRUPTED, return nonzero and set *PORT to the receive right
-   being waited on.  */
-
-extern int _hurdsig_rcv_interrupted_p (struct machine_thread_all_state *state,
-                                      mach_port_t *port);
-
 /* Set up STATE with a thread state that, when resumed, is
    like `longjmp (_hurd_sigthread_fault_env, 1)'.  */
 
 extern void _hurd_initialize_fault_recovery_state (void *state);
 
+/* Set up STATE to do the equivalent of `longjmp (ENV, VAL);'.  */
+
+extern void _hurd_longjmp_thread_state (void *state, jmp_buf env, int value);
 
 /* Function run for SIGINFO when its action is SIG_DFL and the current
    process is the session leader.  */
@@ -275,50 +280,10 @@ extern void _hurd_initialize_fault_recovery_state (void *state);
 extern void _hurd_siginfo_handler (int);
 
 
-/* Perform interruptible RPC CALL on PORT.
-   The call should use 
-   The args in CALL should be constant or local variable refs.
-   They may be evaluated many times, and must not change.
-   PORT must not be deallocated before this RPC is finished.  */
-#define        HURD_EINTR_RPC(port, call)                                            \
-  ({                                                                         \
-    __label__ __do_call;       /* Give this label block scope.  */           \
-    error_t __err;                                                           \
-    struct hurd_sigstate *__ss = _hurd_self_sigstate_unlocked ();            \
-    __do_call:                                                               \
-    /* Tell the signal thread that we are doing an interruptible RPC on              \
-       this port.  If we get a signal and should return EINTR, the signal     \
-       thread will set this variable to MACH_PORT_NULL.  The RPC might       \
-       return EINTR when some other thread gets a signal, in which case we    \
-       want to restart our call.  */                                         \
-    __ss->intr_port = (port);                                                \
-    /* A signal may arrive here, after intr_port is set, but before the              \
-       mach_msg system call.  The signal handler might do an interruptible    \
-       RPC, and clobber intr_port; then it would not be set properly when     \
-       we actually did send the RPC, and a later signal wouldn't interrupt    \
-       that RPC.  So, _hurd_setup_sighandler saves intr_port in the          \
-       sigcontext, and sigreturn restores it.  */                            \
-    switch (__err = (call))                                                  \
-      {                                                                              \
-      case EINTR:              /* RPC went out and was interrupted.  */      \
-      case MACH_SEND_INTERRUPTED: /* RPC didn't get out.  */                 \
-       if (__ss->intr_port != MACH_PORT_NULL)                                \
-         /* If this signal was for us and it should interrupt calls, the     \
-             signal thread will have cleared SS->intr_port.  Since it's not   \
-             cleared, the signal was for another thread, or SA_RESTART is     \
-             set.  Restart the interrupted call.  */                         \
-         goto __do_call;                                                     \
-       /* FALLTHROUGH */                                                     \
-      case MACH_RCV_PORT_DIED:                                               \
-       /* Server didn't respond to interrupt_operation,                      \
-          so the signal thread destroyed the reply port.  */                 \
-       __err = EINTR;                                                        \
-       break;                                                                \
-      default:                 /* Quiet -Wswitch-enum.  */                   \
-      }                                                                              \
-    __ss->intr_port = MACH_PORT_NULL;                                        \
-    __err;                                                                   \
-  })                                                                         \
+/* Milliseconds to wait for an interruptible RPC to return after
+   `interrupt_operation'.  */
+
+extern mach_msg_timeout_t _hurd_interrupted_rpc_timeout;
 
 
 /* Mask of signals that cannot be caught, blocked, or ignored.  */
@@ -329,12 +294,14 @@ extern void _hurd_siginfo_handler (int);
    Each argument is an expression which returns an error code; each
    expression may be evaluated several times.  FETCH_MSGPORT_EXPR should
    fetch the appropriate message port and store it in the local variable
-   `msgport'.  FETCH_REFPORT_EXPR should fetch the appropriate message port
-   and store it in the local variable `refport' (if no reference port is
-   needed in the call, then FETCH_REFPORT_EXPR should be simply
-   KERN_SUCCESS or 0).  Both of these are assumed to create user
-   references, which this macro deallocates.  RPC_EXPR should perform the
-   desired RPC operation using `msgport' and `refport'.
+   `msgport'; it will be deallocated after use.  FETCH_REFPORT_EXPR should
+   fetch the appropriate message port and store it in the local variable
+   `refport' (if no reference port is needed in the call, then
+   FETCH_REFPORT_EXPR should be simply KERN_SUCCESS or 0); if
+   DEALLOC_REFPORT evaluates to nonzero it will be deallocated after use,
+   otherwise the FETCH_REFPORT_EXPR must take care of user references to
+   `refport'.  RPC_EXPR should perform the desired RPC operation using
+   `msgport' and `refport'.
 
    The reason for the complexity is that a process's message port and
    reference port may change between fetching those ports and completing an
@@ -345,7 +312,9 @@ extern void _hurd_siginfo_handler (int);
    either of these cases, we retry the entire operation, discarding the old
    message and reference ports and fetch them anew.  */
 
-#define HURD_MSGPORT_RPC(fetch_msgport_expr, fetch_refport_expr, rpc_expr)   \
+#define HURD_MSGPORT_RPC(fetch_msgport_expr,                                 \
+                        fetch_refport_expr, dealloc_refport,                 \
+                        rpc_expr)                                            \
 ({                                                                           \
     error_t __err;                                                           \
     mach_port_t msgport, refport = MACH_PORT_NULL;                           \
@@ -363,37 +332,12 @@ extern void _hurd_siginfo_handler (int);
          }                                                                   \
        __err = (rpc_expr);                                                   \
        __mach_port_deallocate (__mach_task_self (), msgport);                \
-       if (refport != MACH_PORT_NULL)                                        \
+       if ((dealloc_refport) && refport != MACH_PORT_NULL)                   \
          __mach_port_deallocate (__mach_task_self (), refport);              \
       } while (__err == MACH_SEND_INVALID_DEST ||                            \
               __err == MIG_SERVER_DIED);                                     \
     __err;                                                                   \
 })
-\f
-/* Some other parts of the library need to preempt signals, to detect
-   errors that should not result in a POSIX signal.  For example, when
-   some mapped region of memory is used, an extraneous SIGSEGV might be
-   generated when the mapping server returns an error for a page fault.  */
-
-struct hurd_signal_preempt
-  {
-    /* Function to examine a thread receiving a given signal.  The handler
-       is called even for blocked signals.  This function is run in the
-       signal thread, with THREAD's sigstate locked; it should be as simple
-       and robust as possible.  THREAD is the thread which is about to
-       receive the signal.  SIGNO and SIGCODE would be passed to the normal
-       handler.
-
-       If the return value is SIG_DFL, normal signal processing continues.
-       If it is SIG_IGN, the signal is ignored.
-       Any other value is used in place of the normal handler.  */
-    sighandler_t (*handler) (thread_t thread, int signo, int sigcode);
-    int first, last;           /* Range of sigcodes this handler wants.  */
-    struct hurd_signal_preempt *next; /* Next handler on the chain. */
-  };
-
-extern struct hurd_signal_preempt *_hurd_signal_preempt[NSIG];
-extern struct mutex _hurd_signal_preempt_lock;
 
 
 #endif /* hurd/signal.h */