X-Git-Url: http://git.megacz.com/?p=ghc-hetmet.git;a=blobdiff_plain;f=rts%2Fposix%2FSignals.c;h=9f5bf9f370edc4aba35028054b4716d7fd295b2b;hp=5f5f77fd39fd6d40b1b7b5894ab7e7c0026a32c6;hb=cf5905ea24904cf73a041fd7535e8723a668cb9a;hpb=0065d5ab628975892cea1ec7303f968c3338cbe1 diff --git a/rts/posix/Signals.c b/rts/posix/Signals.c index 5f5f77f..9f5bf9f 100644 --- a/rts/posix/Signals.c +++ b/rts/posix/Signals.c @@ -6,16 +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 "Stable.h" #ifdef alpha_HOST_ARCH # if defined(linux_HOST_OS) @@ -33,13 +32,26 @@ # include #endif +#ifdef HAVE_ERRNO_H +# include +#endif + +#ifdef HAVE_EVENTFD_H +# include +#endif + +#ifdef HAVE_TERMIOS_H +#include +#endif + #include +#include /* 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 @@ -58,7 +70,7 @@ static nat n_haskell_handlers = 0; * -------------------------------------------------------------------------- */ static void -more_handlers(I_ sig) +more_handlers(int sig) { StgInt i; @@ -77,68 +89,90 @@ 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; +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; } -#if !defined(THREADED_RTS) - -#define N_PENDING_HANDLERS 16 +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; +} -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 @@ -148,18 +182,31 @@ 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) + 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 @@ -195,7 +242,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]) { @@ -203,19 +252,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); + 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 */ } /* ----------------------------------------------------------------------------- @@ -229,6 +268,10 @@ void initUserSignals(void) { sigemptyset(&userSignals); +#ifndef THREADED_RTS + getStablePtr((StgPtr)&base_GHCziConcziSignal_runHandlers_closure); + // needed to keep runHandler alive +#endif } void @@ -261,10 +304,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; @@ -285,26 +332,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: @@ -318,25 +358,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; } /* ----------------------------------------------------------------------------- @@ -347,16 +400,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_GHCziConcziSignal_runHandlers_closure, + rts_mkPtr(cap, info)), + rts_mkInt(cap, info->si_signo)))); } unblockUserSignals(); @@ -365,37 +434,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"); @@ -411,24 +461,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. @@ -440,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 @@ -455,34 +568,22 @@ 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); 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. @@ -498,13 +599,53 @@ initDefaultHandlers() 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 + // 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 +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"); + } + + set_sigtstp_action(rtsFalse); +} + +void +freeSignalHandlers(void) { + if (signal_handlers != NULL) { + stgFree(signal_handlers); + } } #endif /* RTS_USER_SIGNALS */