*
* ---------------------------------------------------------------------------*/
-/* 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 "Stable.h"
#ifdef alpha_HOST_ARCH
# if defined(linux_HOST_OS)
# include <signal.h>
#endif
+#ifdef HAVE_ERRNO_H
+# include <errno.h>
+#endif
+
+#ifdef HAVE_EVENTFD_H
+# include <sys/eventfd.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
* installing a SIGCHLD handler.
*/
-StgInt nocldstop = 0;
+HsInt nocldstop = 0;
/* -----------------------------------------------------------------------------
* The table of signal handlers
* -------------------------------------------------------------------------- */
static void
-more_handlers(I_ sig)
+more_handlers(int sig)
{
StgInt i;
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;
+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 System.Event.Control depends on it.
+ io_manager_wakeup_fd = fd;
}
-#if !defined(THREADED_RTS)
-
-#define N_PENDING_HANDLERS 16
+void
+setIOManagerControlFd (int fd)
+{
+ // only called when THREADED_RTS, but unconditionally
+ // compiled here because System.Event.Control depends on it.
+ io_manager_control_fd = fd;
+}
-StgPtr pending_handler_buf[N_PENDING_HANDLERS];
-StgPtr *next_pending_handler = pending_handler_buf;
+void
+ioManagerWakeup (void)
+{
+ int r;
+ // Wake up the IO Manager thread by sending a byte down its pipe
+ 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_wakeup_fd, &byte, 1);
+#endif
+ if (r == -1) { sysErrorBelch("ioManagerWakeup: write"); }
+ }
+}
-#endif /* THREADED_RTS */
+#if defined(THREADED_RTS)
+void
+ioManagerDie (void)
+{
+ int r;
+ // Ask the IO Manager thread to exit
+ if (io_manager_control_fd >= 0) {
+ StgWord8 byte = (StgWord8)IO_MANAGER_DIE;
+ r = write(io_manager_control_fd, &byte, 1);
+ if (r == -1) { sysErrorBelch("ioManagerDie: write"); }
+ io_manager_control_fd = -1;
+ io_manager_wakeup_fd = -1;
+ }
+}
-/* -----------------------------------------------------------------------------
- * 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.
- * -------------------------------------------------------------------------- */
+Capability *
+ioManagerStartCap (Capability *cap)
+{
+ return rts_evalIO(
+ cap,&base_GHCziConcziIO_ensureIOManagerIsRunning_closure,NULL);
+}
-static void
-cont_handler(int sig STG_UNUSED)
+void
+ioManagerStart (void)
{
- setNonBlockingFd(0);
+ // Make sure the IO manager thread is running
+ Capability *cap;
+ if (io_manager_control_fd < 0 || io_manager_wakeup_fd < 0) {
+ cap = rts_lock();
+ cap = ioManagerStartCap(cap);
+ rts_unlock(cap);
+ }
}
+#endif
+
+#if !defined(THREADED_RTS)
+
+#define N_PENDING_HANDLERS 16
+
+siginfo_t pending_handler_buf[N_PENDING_HANDLERS];
+siginfo_t *next_pending_handler = pending_handler_buf;
+
+#endif /* THREADED_RTS */
/* -----------------------------------------------------------------------------
* Low-level signal handler
* -------------------------------------------------------------------------- */
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)
+ if (io_manager_control_fd != -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;
+
+ 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);
+ }
}
// 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
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]) {
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);
+ contextSwitchCapability(&MainCapability);
- // *always* do the SIGCONT handler, even if the user overrides it.
- if (sig == SIGCONT) {
- cont_handler(sig);
- }
-
- context_switch = 1;
+#endif /* THREADED_RTS */
}
/* -----------------------------------------------------------------------------
initUserSignals(void)
{
sigemptyset(&userSignals);
+#ifndef THREADED_RTS
+ getStablePtr((StgPtr)&base_GHCziConcziSignal_runHandlers_closure);
+ // needed to keep runHandler alive
+#endif
}
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;
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:
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;
}
/* -----------------------------------------------------------------------------
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_GHCziConcziSignal_runHandlers_closure,
+ rts_mkPtr(cap, info)),
+ rts_mkInt(cap, info->si_signo))));
}
unblockUserSignals();
/* ----------------------------------------------------------------------------
* 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");
* 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.
* 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);
action.sa_flags = 0;
if (sigaction(SIGINT, &action, &oact) != 0) {
- errorBelch("warning: failed to install SIGINT handler");
+ sysErrorBelch("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) {
- errorBelch("warning: failed to install SIGCONT handler");
- }
-
// install the SIGFPE handler
// In addition to handling SIGINT, also handle SIGFPE by ignoring it.
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
if (sigaction(SIGFPE, &action, &oact) != 0) {
- errorBelch("warning: failed to install SIGFPE handler");
+ sysErrorBelch("warning: failed to install SIGFPE handler");
}
#endif
#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
+freeSignalHandlers(void) {
+ if (signal_handlers != NULL) {
+ stgFree(signal_handlers);
+ }
}
#endif /* RTS_USER_SIGNALS */