RTS tidyup sweep, first phase
[ghc-hetmet.git] / rts / posix / Signals.c
index abbb97a..6600657 100644 (file)
@@ -6,18 +6,15 @@
  *
  * ---------------------------------------------------------------------------*/
 
-/* This is non-Posix-compliant.
-   #include "PosixSource.h" 
-*/
+#include "PosixSource.h" 
 #include "Rts.h"
-#include "SchedAPI.h"
+
 #include "Schedule.h"
 #include "RtsSignals.h"
-#include "posix/Signals.h"
+#include "Signals.h"
 #include "RtsUtils.h"
-#include "RtsFlags.h"
 #include "Prelude.h"
-#include "ThrIOManager.h"
+#include "Stable.h"
 
 #ifdef alpha_HOST_ARCH
 # if defined(linux_HOST_OS)
 # include <signal.h>
 #endif
 
+#ifdef HAVE_ERRNO_H
+# include <errno.h>
+#endif
+
 #include <stdlib.h>
+#include <string.h>
 
 /* This curious flag is provided for the benefit of the Haskell binding
  * to POSIX.1 to control whether or not to include SA_NOCLDSTOP when
@@ -60,7 +62,7 @@ static nat n_haskell_handlers = 0;
  * -------------------------------------------------------------------------- */
 
 static void
-more_handlers(I_ sig)
+more_handlers(int sig)
 {
     StgInt i;
 
@@ -79,33 +81,6 @@ more_handlers(I_ sig)
     nHandlers = sig + 1;
 }
 
-/* -----------------------------------------------------------------------------
- * Pending Handlers
- *
- * The mechanism for starting handlers differs between the threaded
- * (THREADED_RTS) and non-threaded versions of the RTS.
- *
- * When the RTS is single-threaded, we just write the pending signal
- * handlers into a buffer, and start a thread for each one in the
- * scheduler loop.
- *
- * When THREADED_RTS, the problem is that signals might be
- * delivered to multiple threads, so we would need to synchronise
- * access to pending_handler_buf somehow.  Using thread
- * synchronisation from a signal handler isn't possible in general
- * (some OSs support it, eg. MacOS X, but not all).  So instead:
- *
- *   - the signal handler writes the signal number into the pipe
- *     managed by the IO manager thread (see GHC.Conc).
- *   - the IO manager picks up the signal number and calls
- *     startSignalHandler() to start the thread.
- *
- * This also has the nice property that we don't need to arrange to
- * wake up a worker task to start the signal handler: the IO manager
- * wakes up when we write into the pipe.
- *
- * -------------------------------------------------------------------------- */
-
 // Here's the pipe into which we will send our signals
 static int io_manager_pipe = -1;
 
@@ -117,27 +92,35 @@ setIOManagerPipe (int fd)
 {
     // only called when THREADED_RTS, but unconditionally
     // compiled here because GHC.Conc depends on it.
-    io_manager_pipe = fd;
+    if (io_manager_pipe < 0) {
+        io_manager_pipe = fd;
+    }
 }
 
 #if defined(THREADED_RTS)
 void
 ioManagerWakeup (void)
 {
+    int r;
     // Wake up the IO Manager thread by sending a byte down its pipe
     if (io_manager_pipe >= 0) {
        StgWord8 byte = (StgWord8)IO_MANAGER_WAKEUP;
-       write(io_manager_pipe, &byte, 1);
+       r = write(io_manager_pipe, &byte, 1);
+        if (r == -1) { sysErrorBelch("ioManagerWakeup: write"); }
     }
 }
 
 void
 ioManagerDie (void)
 {
+    int r;
     // Ask the IO Manager thread to exit
     if (io_manager_pipe >= 0) {
        StgWord8 byte = (StgWord8)IO_MANAGER_DIE;
-       write(io_manager_pipe, &byte, 1);
+       r = write(io_manager_pipe, &byte, 1);
+        if (r == -1) { sysErrorBelch("ioManagerDie: write"); }
+        close(io_manager_pipe);
+        io_manager_pipe = -1;
     }
 }
 
@@ -148,7 +131,7 @@ ioManagerStart (void)
     Capability *cap;
     if (io_manager_pipe < 0) {
        cap = rts_lock();
-       rts_evalIO(cap,&base_GHCziConc_ensureIOManagerIsRunning_closure,NULL);
+       cap = rts_evalIO(cap,&base_GHCziConc_ensureIOManagerIsRunning_closure,NULL);
        rts_unlock(cap);
     }
 }
@@ -158,28 +141,12 @@ ioManagerStart (void)
 
 #define N_PENDING_HANDLERS 16
 
-StgPtr pending_handler_buf[N_PENDING_HANDLERS];
-StgPtr *next_pending_handler = pending_handler_buf;
+siginfo_t pending_handler_buf[N_PENDING_HANDLERS];
+siginfo_t *next_pending_handler = pending_handler_buf;
 
 #endif /* THREADED_RTS */
 
 /* -----------------------------------------------------------------------------
- * SIGCONT handler
- *
- * It seems that shells tend to put stdin back into blocking mode
- * following a suspend/resume of the process.  Here we arrange to put
- * it back into non-blocking mode.  We don't do anything to
- * stdout/stderr because these handles don't get put into non-blocking
- * mode at all - see the comments on stdout/stderr in PrelHandle.hsc.
- * -------------------------------------------------------------------------- */
-
-static void
-cont_handler(int sig STG_UNUSED)
-{
-    setNonBlockingFd(0);
-}
-
-/* -----------------------------------------------------------------------------
  * Low-level signal handler
  *
  * Places the requested handler on a stack of pending handlers to be
@@ -187,18 +154,24 @@ cont_handler(int sig STG_UNUSED)
  * -------------------------------------------------------------------------- */
 
 static void
-generic_handler(int sig)
+generic_handler(int sig USED_IF_THREADS,
+                siginfo_t *info,
+                void *p STG_UNUSED)
 {
-    sigset_t signals;
-
 #if defined(THREADED_RTS)
 
     if (io_manager_pipe != -1)
     {
-       // Write the signal number into the pipe as a single byte.  We
-       // hope that signals fit into a byte...
-       StgWord8 csig = (StgWord8)sig;
-       write(io_manager_pipe, &csig, 1);
+        StgWord8 buf[sizeof(siginfo_t) + 1];
+        int r;
+
+        buf[0] = sig;
+        memcpy(buf+1, info, sizeof(siginfo_t));
+       r = write(io_manager_pipe, buf, sizeof(siginfo_t)+1);
+        if (r == -1 && errno == EAGAIN)
+        {
+            errorBelch("lost signal due to full pipe: %d\n", sig);
+        }
     }
     // If the IO manager hasn't told us what the FD of the write end
     // of its pipe is, there's not much we can do here, so just ignore
@@ -234,7 +207,9 @@ generic_handler(int sig)
        circumstances, depending on the signal.  
     */
 
-    *next_pending_handler++ = deRefStablePtr((StgStablePtr)signal_handlers[sig]);
+    memcpy(next_pending_handler, info, sizeof(siginfo_t));
+
+    next_pending_handler++;
 
     // stack full?
     if (next_pending_handler == &pending_handler_buf[N_PENDING_HANDLERS]) {
@@ -242,19 +217,9 @@ generic_handler(int sig)
        stg_exit(EXIT_FAILURE);
     }
     
-#endif /* THREADED_RTS */
-
-    // re-establish the signal handler, and carry on
-    sigemptyset(&signals);
-    sigaddset(&signals, sig);
-    sigprocmask(SIG_UNBLOCK, &signals, NULL);
-
-    // *always* do the SIGCONT handler, even if the user overrides it.
-    if (sig == SIGCONT) {
-       cont_handler(sig);
-    }
+    contextSwitchCapability(&MainCapability);
 
-    context_switch = 1;
+#endif /* THREADED_RTS */
 }
 
 /* -----------------------------------------------------------------------------
@@ -268,6 +233,10 @@ void
 initUserSignals(void)
 {
     sigemptyset(&userSignals);
+#ifndef THREADED_RTS
+    getStablePtr((StgPtr)&base_GHCziConc_runHandlers_closure); 
+    // needed to keep runHandler alive
+#endif
 }
 
 void
@@ -300,10 +269,14 @@ awaitUserSignals(void)
 
 /* -----------------------------------------------------------------------------
  * Install a Haskell signal handler.
+ *
+ * We should really do this in Haskell in GHC.Conc, and share the
+ * signal_handlers array with the one there.
+ *
  * -------------------------------------------------------------------------- */
 
 int
-stg_sig_install(int sig, int spi, StgStablePtr *handler, void *mask)
+stg_sig_install(int sig, int spi, void *mask)
 {
     sigset_t signals, osignals;
     struct sigaction action;
@@ -324,26 +297,19 @@ stg_sig_install(int sig, int spi, StgStablePtr *handler, void *mask)
     
     switch(spi) {
     case STG_SIG_IGN:
-       signal_handlers[sig] = STG_SIG_IGN;
-       sigdelset(&userSignals, sig);
         action.sa_handler = SIG_IGN;
        break;
-       
+
     case STG_SIG_DFL:
-       signal_handlers[sig] = STG_SIG_DFL;
-       sigdelset(&userSignals, sig);
         action.sa_handler = SIG_DFL;
        break;
 
-    case STG_SIG_HAN:
     case STG_SIG_RST:
-       signal_handlers[sig] = (StgInt)*handler;
-       sigaddset(&userSignals, sig);
-       action.sa_handler = generic_handler;
-       if (spi == STG_SIG_RST) {
-           action.sa_flags = SA_RESETHAND;
-       }
-       n_haskell_handlers++;
+        action.sa_flags |= SA_RESETHAND;
+        /* fall through */
+    case STG_SIG_HAN:
+       action.sa_sigaction = generic_handler;
+        action.sa_flags |= SA_SIGINFO;
        break;
 
     default:
@@ -357,25 +323,38 @@ stg_sig_install(int sig, int spi, StgStablePtr *handler, void *mask)
 
     action.sa_flags |= sig == SIGCHLD && nocldstop ? SA_NOCLDSTOP : 0;
 
-    if (sigaction(sig, &action, NULL) || 
-       sigprocmask(SIG_SETMASK, &osignals, NULL)) 
+    if (sigaction(sig, &action, NULL))
     {
-       // need to return an error code, so avoid a stable pointer leak
-       // by freeing the previous handler if there was one.
-       if (previous_spi >= 0) {
-           freeStablePtr(stgCast(StgStablePtr,signal_handlers[sig]));
-           n_haskell_handlers--;
-       }
+        errorBelch("sigaction");
        return STG_SIG_ERR;
     }
 
-    if (previous_spi == STG_SIG_DFL || previous_spi == STG_SIG_IGN
-       || previous_spi == STG_SIG_ERR) {
-       return previous_spi;
-    } else {
-       *handler = (StgStablePtr)previous_spi;
-       return STG_SIG_HAN;
+    signal_handlers[sig] = spi;
+
+    switch(spi) {
+    case STG_SIG_RST:
+    case STG_SIG_HAN:
+       sigaddset(&userSignals, sig);
+        if (previous_spi != STG_SIG_HAN && previous_spi != STG_SIG_RST) {
+            n_haskell_handlers++;
+        }
+       break;
+
+    default:
+       sigdelset(&userSignals, sig);
+        if (previous_spi == STG_SIG_HAN || previous_spi == STG_SIG_RST) {
+            n_haskell_handlers--;
+        }
+        break;
+    }
+
+    if (sigprocmask(SIG_SETMASK, &osignals, NULL))
+    {
+        errorBelch("sigprocmask");
+       return STG_SIG_ERR;
     }
+
+    return previous_spi;
 }
 
 /* -----------------------------------------------------------------------------
@@ -386,16 +365,32 @@ stg_sig_install(int sig, int spi, StgStablePtr *handler, void *mask)
 void
 startSignalHandlers(Capability *cap)
 {
+  siginfo_t *info;
+  int sig;
+
   blockUserSignals();
   
   while (next_pending_handler != pending_handler_buf) {
 
     next_pending_handler--;
 
+    sig = next_pending_handler->si_signo;
+    if (signal_handlers[sig] == STG_SIG_DFL) {
+        continue; // handler has been changed.
+    }
+
+    info = stgMallocBytes(sizeof(siginfo_t), "startSignalHandlers"); 
+           // freed by runHandler
+    memcpy(info, next_pending_handler, sizeof(siginfo_t));
+
     scheduleThread (cap,
        createIOThread(cap,
                       RtsFlags.GcFlags.initialStkSize, 
-                      (StgClosure *) *next_pending_handler));
+                       rts_apply(cap,
+                                 rts_apply(cap,
+                                           &base_GHCziConc_runHandlers_closure,
+                                           rts_mkPtr(cap, info)),
+                                 rts_mkInt(cap, info->si_signo))));
   }
 
   unblockUserSignals();
@@ -404,37 +399,18 @@ startSignalHandlers(Capability *cap)
 
 /* ----------------------------------------------------------------------------
  * Mark signal handlers during GC.
- *
- * We do this rather than trying to start all the signal handlers
- * prior to GC, because that requires extra heap for the new threads.
- * Signals must be blocked (see blockUserSignals() above) during GC to
- * avoid race conditions.
  * -------------------------------------------------------------------------- */
 
-#if !defined(THREADED_RTS)
 void
-markSignalHandlers (evac_fn evac)
-{
-    StgPtr *p;
-
-    p = next_pending_handler;
-    while (p != pending_handler_buf) {
-       p--;
-       evac((StgClosure **)p);
-    }
-}
-#else
-void
-markSignalHandlers (evac_fn evac STG_UNUSED)
+markSignalHandlers (evac_fn evac STG_UNUSED, void *user STG_UNUSED)
 {
+    // nothing to do
 }
-#endif
 
 #else /* !RTS_USER_SIGNALS */
 StgInt 
 stg_sig_install(StgInt sig STG_UNUSED,
                StgInt spi STG_UNUSED,
-               StgStablePtr* handler STG_UNUSED,
                void* mask STG_UNUSED)
 {
   //barf("User signals not supported");
@@ -450,24 +426,9 @@ stg_sig_install(StgInt sig STG_UNUSED,
  * We like to shutdown nicely after receiving a SIGINT, write out the
  * stats, write profiling info, close open files and flush buffers etc.
  * -------------------------------------------------------------------------- */
-#ifdef SMP
-pthread_t startup_guy;
-#endif
-
 static void
 shutdown_handler(int sig STG_UNUSED)
 {
-#ifdef SMP
-    // if I'm a worker thread, send this signal to the guy who
-    // originally called startupHaskell().  Since we're handling
-    // the signal, it won't be a "send to all threads" type of signal
-    // (according to the POSIX threads spec).
-    if (pthread_self() != startup_guy) {
-       pthread_kill(startup_guy, sig);
-       return;
-    }
-#endif
-
     // If we're already trying to interrupt the RTS, terminate with
     // extreme prejudice.  So the first ^C tries to exit the program
     // cleanly, and the second one just kills it.
@@ -494,14 +455,10 @@ shutdown_handler(int sig STG_UNUSED)
  * doesn't seem to do so.
  * -------------------------------------------------------------------------- */
 void
-initDefaultHandlers()
+initDefaultHandlers(void)
 {
     struct sigaction action,oact;
 
-#ifdef SMP
-    startup_guy = pthread_self();
-#endif
-
     // install the SIGINT handler
     action.sa_handler = shutdown_handler;
     sigemptyset(&action.sa_mask);
@@ -514,14 +471,6 @@ initDefaultHandlers()
     siginterrupt(SIGINT, 1);   // isn't this the default? --SDM
 #endif
 
-    // install the SIGCONT handler
-    action.sa_handler = cont_handler;
-    sigemptyset(&action.sa_mask);
-    action.sa_flags = 0;
-    if (sigaction(SIGCONT, &action, &oact) != 0) {
-       sysErrorBelch("warning: failed to install SIGCONT handler");
-    }
-
     // install the SIGFPE handler
 
     // In addition to handling SIGINT, also handle SIGFPE by ignoring it.
@@ -544,6 +493,33 @@ initDefaultHandlers()
 #ifdef alpha_HOST_ARCH
     ieee_set_fp_control(0);
 #endif
+
+    // ignore SIGPIPE; see #1619
+    action.sa_handler = SIG_IGN;
+    sigemptyset(&action.sa_mask);
+    action.sa_flags = 0;
+    if (sigaction(SIGPIPE, &action, &oact) != 0) {
+       sysErrorBelch("warning: failed to install SIGPIPE handler");
+    }
+}
+
+void
+resetDefaultHandlers(void)
+{
+    struct sigaction action;
+
+    action.sa_handler = SIG_DFL;
+    sigemptyset(&action.sa_mask);
+    action.sa_flags = 0;
+
+    // restore SIGINT
+    if (sigaction(SIGINT, &action, NULL) != 0) {
+       sysErrorBelch("warning: failed to uninstall SIGINT handler");
+    }
+    // restore SIGPIPE
+    if (sigaction(SIGPIPE, &action, NULL) != 0) {
+       sysErrorBelch("warning: failed to uninstall SIGPIPE handler");
+    }
 }
 
 void