/* -----------------------------------------------------------------------------
- * $Id: Signals.c,v 1.22 2001/10/31 10:34:29 simonmar Exp $
*
* (c) The GHC Team, 1998-1999
*
#include "Signals.h"
#include "RtsUtils.h"
#include "RtsFlags.h"
-#include "StablePriv.h"
-#ifdef alpha_TARGET_ARCH
-#include <machine/fpu.h>
+#ifdef alpha_HOST_ARCH
+# if defined(linux_HOST_OS)
+# include <asm/fpu.h>
+# else
+# include <machine/fpu.h>
+# endif
#endif
-#ifndef mingw32_TARGET_OS
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#ifdef HAVE_SIGNAL_H
+# include <signal.h>
+#endif
-#ifndef PAR
+#include <stdlib.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
+ * installing a SIGCHLD handler.
+ */
+StgInt nocldstop = 0;
+
+/* -----------------------------------------------------------------------------
+ * The table of signal handlers
+ * -------------------------------------------------------------------------- */
+
+#if defined(RTS_USER_SIGNALS)
/* SUP: The type of handlers is a little bit, well, doubtful... */
static StgInt *handlers = NULL; /* Dynamically grown array of signal handlers */
static StgInt nHandlers = 0; /* Size of handlers array */
-#define N_PENDING_HANDLERS 16
-
-StgPtr pending_handler_buf[N_PENDING_HANDLERS];
-StgPtr *next_pending_handler = pending_handler_buf;
-
-StgInt nocldstop = 0;
+static nat n_haskell_handlers = 0;
/* -----------------------------------------------------------------------------
* Allocate/resize the table of signal handlers.
return;
if (handlers == NULL)
- handlers = (StgInt *) malloc((sig + 1) * sizeof(StgInt));
+ handlers = (StgInt *)stgMallocBytes((sig + 1) * sizeof(StgInt), "more_handlers");
else
- handlers = (StgInt *) realloc(handlers, (sig + 1) * sizeof(StgInt));
+ handlers = (StgInt *)stgReallocBytes(handlers, (sig + 1) * sizeof(StgInt), "more_handlers");
- if (handlers == NULL) {
- // don't fflush(stdout); WORKAROUND bug in Linux glibc
- barf("VM exhausted (in more_handlers)");
- }
for(i = nHandlers; i <= sig; i++)
// Fill in the new slots with default actions
handlers[i] = STG_SIG_DFL;
}
/* -----------------------------------------------------------------------------
+ * Pending Handlers
+ *
+ * The mechanism for starting handlers differs between the threaded
+ * (RTS_SUPPORTS_THREADS) 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 RTS_SUPPORTS_THREADS, 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;
+
+void
+setIOManagerPipe (int fd)
+{
+ // only called when RTS_SUPPORTS_THREADS, but unconditionally
+ // compiled here because GHC.Conc depends on it.
+ io_manager_pipe = fd;
+}
+
+#if !defined(RTS_SUPPORTS_THREADS)
+
+#define N_PENDING_HANDLERS 16
+
+StgPtr pending_handler_buf[N_PENDING_HANDLERS];
+StgPtr *next_pending_handler = pending_handler_buf;
+
+#endif /* RTS_SUPPORTS_THREADS */
+
+/* -----------------------------------------------------------------------------
* SIGCONT handler
*
* It seems that shells tend to put stdin back into blocking mode
{
sigset_t signals;
+#if defined(RTS_SUPPORTS_THREADS)
+
+ 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);
+ }
+ // 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
+ // the signal..
+
+#else /* not RTS_SUPPORTS_THREADS */
+
/* Can't call allocate from here. Probably can't call malloc
either. However, we have to schedule a new thread somehow.
// stack full?
if (next_pending_handler == &pending_handler_buf[N_PENDING_HANDLERS]) {
- barf("too many pending signals");
+ errorBelch("too many pending signals");
+ stg_exit(EXIT_FAILURE);
}
+#endif /* RTS_SUPPORTS_THREADS */
+
// re-establish the signal handler, and carry on
sigemptyset(&signals);
sigaddset(&signals, sig);
void
blockUserSignals(void)
{
- sigprocmask(SIG_SETMASK, &userSignals, &savedSignals);
+ sigprocmask(SIG_BLOCK, &userSignals, &savedSignals);
}
void
sigprocmask(SIG_SETMASK, &savedSignals, NULL);
}
+rtsBool
+anyUserHandlers(void)
+{
+ return n_haskell_handlers != 0;
+}
+
+#if !defined(RTS_SUPPORTS_THREADS)
+void
+awaitUserSignals(void)
+{
+ while (!signals_pending() && !interrupted) {
+ pause();
+ }
+}
+#endif
/* -----------------------------------------------------------------------------
* Install a Haskell signal handler.
* -------------------------------------------------------------------------- */
-StgInt
-stg_sig_install(StgInt sig, StgInt spi, StgStablePtr handler, sigset_t *mask)
+int
+stg_sig_install(int sig, int spi, StgStablePtr *handler, void *mask)
{
- sigset_t signals;
+ sigset_t signals, osignals;
struct sigaction action;
StgInt previous_spi;
// Block the signal until we figure out what to do
// Count on this to fail if the signal number is invalid
if (sig < 0 || sigemptyset(&signals) ||
- sigaddset(&signals, sig) || sigprocmask(SIG_BLOCK, &signals, NULL)) {
+ sigaddset(&signals, sig) || sigprocmask(SIG_BLOCK, &signals, &osignals)) {
return STG_SIG_ERR;
}
previous_spi = handlers[sig];
+ action.sa_flags = 0;
+
switch(spi) {
case STG_SIG_IGN:
handlers[sig] = STG_SIG_IGN;
break;
case STG_SIG_HAN:
- handlers[sig] = (StgInt)handler;
+ case STG_SIG_RST:
+ 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++;
break;
default:
barf("stg_sig_install: bad spi");
}
- if (mask != 0)
- action.sa_mask = *mask;
+ if (mask != NULL)
+ action.sa_mask = *(sigset_t *)mask;
else
sigemptyset(&action.sa_mask);
- action.sa_flags = sig == SIGCHLD && nocldstop ? SA_NOCLDSTOP : 0;
+ action.sa_flags |= sig == SIGCHLD && nocldstop ? SA_NOCLDSTOP : 0;
if (sigaction(sig, &action, NULL) ||
- sigprocmask(SIG_UNBLOCK, &signals, NULL))
+ sigprocmask(SIG_SETMASK, &osignals, 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,handlers[sig]));
+ n_haskell_handlers--;
}
return STG_SIG_ERR;
}
-
- return previous_spi;
+
+ 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;
+ }
}
/* -----------------------------------------------------------------------------
- * Creating new threads for the pending signal handlers.
+ * Creating new threads for signal handlers.
* -------------------------------------------------------------------------- */
+
+void
+startSignalHandler(int sig) // called by the IO manager, see GHC.Conc
+{
+#if defined(RTS_SUPPORTS_THREADS)
+ // ToDo: fix race window between the time at which the signal is
+ // delivered and the deRefStablePtr() call here. There's no way
+ // to safely uninstall a signal handler.
+ scheduleThread(
+ createIOThread(RtsFlags.GcFlags.initialStkSize,
+ (StgClosure *)deRefStablePtr((StgStablePtr)handlers[sig]))
+ );
+#else
+ (void)sig; /* keep gcc -Wall happy */
+#endif
+}
+
void
startSignalHandlers(void)
{
+#if !defined(RTS_SUPPORTS_THREADS)
blockUserSignals();
while (next_pending_handler != pending_handler_buf) {
}
unblockUserSignals();
+#endif
}
-#else // PAR
-StgInt
-stg_sig_install(StgInt sig, StgInt spi, StgStablePtr handler, sigset_t *mask)
+/* ----------------------------------------------------------------------------
+ * 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(RTS_SUPPORTS_THREADS)
+void
+markSignalHandlers (evac_fn evac)
{
- // don't fflush(stdout); WORKAROUND bug in Linux glibc
- barf("no signal handling support in a parallel implementation");
-}
+ StgPtr *p;
+ p = next_pending_handler;
+ while (p != pending_handler_buf) {
+ p--;
+ evac((StgClosure **)p);
+ }
+}
+#else
void
-startSignalHandlers(void)
+markSignalHandlers (evac_fn evac STG_UNUSED)
{
}
#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");
+ return STG_SIG_DFL;
+}
+
+#endif
+
+#if defined(RTS_USER_SIGNALS)
/* -----------------------------------------------------------------------------
* SIGINT handler.
*
// extreme prejudice. So the first ^C tries to exit the program
// cleanly, and the second one just kills it.
if (interrupted) {
- exit(EXIT_INTERRUPTED);
+ stg_exit(EXIT_INTERRUPTED);
} else {
interruptStgRts();
}
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
if (sigaction(SIGINT, &action, &oact) != 0) {
- prog_belch("warning: failed to install SIGINT handler");
+ errorBelch("warning: failed to install SIGINT handler");
}
+#if defined(HAVE_SIGINTERRUPT)
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) {
- prog_belch("warning: failed to install SIGCONT handler");
+ errorBelch("warning: failed to install SIGCONT handler");
}
// install the SIGFPE handler
+
+ // In addition to handling SIGINT, also handle SIGFPE by ignoring it.
+ // Apparently IEEE requires floating-point exceptions to be ignored by
+ // default, but alpha-dec-osf3 doesn't seem to do so.
+
+ // Commented out by SDM 2/7/2002: this causes an infinite loop on
+ // some architectures when an integer division by zero occurs: we
+ // don't recover from the floating point exception, and the
+ // program just generates another one immediately.
+#if 0
action.sa_handler = SIG_IGN;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
if (sigaction(SIGFPE, &action, &oact) != 0) {
- prog_belch("warning: failed to install SIGFPE handler");
+ errorBelch("warning: failed to install SIGFPE handler");
}
-#ifdef alpha_TARGET_ARCH
+#endif
+
+#ifdef alpha_HOST_ARCH
ieee_set_fp_control(0);
#endif
}
-#endif /*! mingw32_TARGET_OS */
+#endif /* RTS_USER_SIGNALS */