merge upstream HEAD
[ghc-hetmet.git] / rts / posix / Signals.c
index c1ffb5d..9f5bf9f 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 <errno.h>
 #endif
 
+#ifdef HAVE_EVENTFD_H
+# include <sys/eventfd.h>
+#endif
+
+#ifdef HAVE_TERMIOS_H
+#include <termios.h>
+#endif
+
 #include <stdlib.h>
 #include <string.h>
 
@@ -85,54 +90,76 @@ more_handlers(int sig)
 }
 
 // Here's the pipe into which we will send our signals
-static int io_manager_pipe = -1;
+static int io_manager_wakeup_fd = -1;
+static int io_manager_control_fd = -1;
 
 #define IO_MANAGER_WAKEUP 0xff
 #define IO_MANAGER_DIE    0xfe
+#define IO_MANAGER_SYNC   0xfd
 
 void
-setIOManagerPipe (int fd)
+setIOManagerWakeupFd (int fd)
 {
     // only called when THREADED_RTS, but unconditionally
-    // compiled here because GHC.Conc depends on it.
-    io_manager_pipe = fd;
+    // compiled here because GHC.Event.Control depends on it.
+    io_manager_wakeup_fd = fd;
+}
+
+void
+setIOManagerControlFd (int fd)
+{
+    // only called when THREADED_RTS, but unconditionally
+    // compiled here because GHC.Event.Control depends on it.
+    io_manager_control_fd = 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) {
+    if (io_manager_wakeup_fd >= 0) {
+#if defined(HAVE_EVENTFD)
+       StgWord64 n = (StgWord64)IO_MANAGER_WAKEUP;
+       r = write(io_manager_wakeup_fd, (char *) &n, 8);
+#else
        StgWord8 byte = (StgWord8)IO_MANAGER_WAKEUP;
-       r = write(io_manager_pipe, &byte, 1);
+       r = write(io_manager_wakeup_fd, &byte, 1);
+#endif
         if (r == -1) { sysErrorBelch("ioManagerWakeup: write"); }
     }
 }
 
+#if defined(THREADED_RTS)
 void
 ioManagerDie (void)
 {
     int r;
     // Ask the IO Manager thread to exit
-    if (io_manager_pipe >= 0) {
+    if (io_manager_control_fd >= 0) {
        StgWord8 byte = (StgWord8)IO_MANAGER_DIE;
-       r = write(io_manager_pipe, &byte, 1);
+       r = write(io_manager_control_fd, &byte, 1);
         if (r == -1) { sysErrorBelch("ioManagerDie: write"); }
-        close(io_manager_pipe);
-        io_manager_pipe = -1;
+        io_manager_control_fd = -1;
+        io_manager_wakeup_fd = -1;
     }
 }
 
+Capability *
+ioManagerStartCap (Capability *cap)
+{
+    return rts_evalIO(
+        cap,&base_GHCziConcziIO_ensureIOManagerIsRunning_closure,NULL);
+}
+
 void
 ioManagerStart (void)
 {
     // Make sure the IO manager thread is running
     Capability *cap;
-    if (io_manager_pipe < 0) {
+    if (io_manager_control_fd < 0 || io_manager_wakeup_fd < 0) {
        cap = rts_lock();
-       cap = rts_evalIO(cap,&base_GHCziConc_ensureIOManagerIsRunning_closure,NULL);
+       cap = ioManagerStartCap(cap);
        rts_unlock(cap);
     }
 }
@@ -161,14 +188,21 @@ generic_handler(int sig USED_IF_THREADS,
 {
 #if defined(THREADED_RTS)
 
-    if (io_manager_pipe != -1)
+    if (io_manager_control_fd != -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 (info == NULL) {
+           // info may be NULL on Solaris (see #3790)
+           memset(buf+1, 0, sizeof(siginfo_t));
+       } else {
+           memcpy(buf+1, info, sizeof(siginfo_t));
+       }
+
+       r = write(io_manager_control_fd, buf, sizeof(siginfo_t)+1);
         if (r == -1 && errno == EAGAIN)
         {
             errorBelch("lost signal due to full pipe: %d\n", sig);
@@ -235,7 +269,7 @@ initUserSignals(void)
 {
     sigemptyset(&userSignals);
 #ifndef THREADED_RTS
-    getStablePtr((StgPtr)&base_GHCziConc_runHandlers_closure); 
+    getStablePtr((StgPtr)&base_GHCziConcziSignal_runHandlers_closure);
     // needed to keep runHandler alive
 #endif
 }
@@ -389,7 +423,7 @@ startSignalHandlers(Capability *cap)
                       RtsFlags.GcFlags.initialStkSize, 
                        rts_apply(cap,
                                  rts_apply(cap,
-                                           &base_GHCziConc_runHandlers_closure,
+                                           &base_GHCziConcziSignal_runHandlers_closure,
                                            rts_mkPtr(cap, info)),
                                  rts_mkInt(cap, info->si_signo))));
   }
@@ -441,6 +475,84 @@ shutdown_handler(int sig STG_UNUSED)
 }
 
 /* -----------------------------------------------------------------------------
+ * An empty signal handler, currently used for SIGPIPE
+ * -------------------------------------------------------------------------- */
+static void
+empty_handler (int sig STG_UNUSED)
+{
+    // nothing
+}
+
+/* -----------------------------------------------------------------------------
+   SIGTSTP handling
+
+   When a process is suspeended with ^Z and resumed again, the shell
+   makes no attempt to save and restore the terminal settings.  So on
+   resume, any terminal setting modificaions we made (e.g. turning off
+   ICANON due to hSetBuffering NoBuffering) may well be lost.  Hence,
+   we arrange to save and restore the terminal settings ourselves.
+
+   The trick we use is:
+     - catch SIGTSTP
+     - in the handler,  kill(getpid(),SIGTSTP)
+     - when this returns, restore the TTY settings
+   This means we don't have to catch SIGCONT too.
+
+   -------------------------------------------------------------------------- */
+
+static void sigtstp_handler(int sig);
+static void set_sigtstp_action (rtsBool handle);
+
+static void
+sigtstp_handler (int sig)
+{
+    int fd;
+    struct termios ts[3];
+
+    // save the current TTY state for TTYs we modified
+    for (fd = 0; fd <= 2; fd++) {
+        if (__hscore_get_saved_termios(fd) != NULL) {
+            tcgetattr(fd,&ts[fd]);
+        }
+    }
+
+    // de-install the SIGTSTP handler
+    set_sigtstp_action(rtsFalse);
+
+    // really stop the process now
+    {
+        sigset_t mask;
+        sigemptyset(&mask);
+        sigaddset(&mask, sig);
+        sigprocmask(SIG_UNBLOCK, &mask, NULL);
+        kill(getpid(), sig);
+    }
+
+    // on return, restore the TTY state
+    for (fd = 0; fd <= 2; fd++) {
+        if (__hscore_get_saved_termios(fd) != NULL) {
+            tcsetattr(0,TCSANOW,&ts[fd]);
+        }
+    }
+
+    set_sigtstp_action(rtsTrue);
+}
+
+static void
+set_sigtstp_action (rtsBool handle)
+{
+    struct sigaction sa;
+    if (handle) {
+        sa.sa_handler = sigtstp_handler;
+    } else {
+        sa.sa_handler = SIG_DFL;
+    }
+    sa.sa_flags = 0;
+    sigemptyset(&sa.sa_mask);
+    sigaction(SIGTSTP, &sa, NULL);
+}
+
+/* -----------------------------------------------------------------------------
  * Install default signal handlers.
  *
  * The RTS installs a default signal handler for catching
@@ -496,12 +608,16 @@ initDefaultHandlers(void)
 #endif
 
     // ignore SIGPIPE; see #1619
-    action.sa_handler = SIG_IGN;
+    // actually, we use an empty signal handler rather than SIG_IGN,
+    // so that SIGPIPE gets reset to its default behaviour on exec.
+    action.sa_handler = empty_handler;
     sigemptyset(&action.sa_mask);
     action.sa_flags = 0;
     if (sigaction(SIGPIPE, &action, &oact) != 0) {
        sysErrorBelch("warning: failed to install SIGPIPE handler");
     }
+
+    set_sigtstp_action(rtsTrue);
 }
 
 void
@@ -521,6 +637,8 @@ resetDefaultHandlers(void)
     if (sigaction(SIGPIPE, &action, NULL) != 0) {
        sysErrorBelch("warning: failed to uninstall SIGPIPE handler");
     }
+
+    set_sigtstp_action(rtsFalse);
 }
 
 void