X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=rts%2Fposix%2FSignals.c;h=9f5bf9f370edc4aba35028054b4716d7fd295b2b;hb=6cec61d14a324285dbb8ce73d4c7215f1f8d6766;hp=e34190c4393790f80c6880a2835a8af1382b3b55;hpb=addff19a9c6ee89e36cb966988aa9f868ae2e4a6;p=ghc-hetmet.git diff --git a/rts/posix/Signals.c b/rts/posix/Signals.c index e34190c..9f5bf9f 100644 --- a/rts/posix/Signals.c +++ b/rts/posix/Signals.c @@ -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) @@ -35,7 +32,20 @@ # 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 @@ -60,7 +70,7 @@ static nat n_haskell_handlers = 0; * -------------------------------------------------------------------------- */ static void -more_handlers(I_ sig) +more_handlers(int sig) { StgInt i; @@ -79,76 +89,77 @@ 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; +} + +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; - 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; - write(io_manager_pipe, &byte, 1); + r = write(io_manager_control_fd, &byte, 1); + if (r == -1) { sysErrorBelch("ioManagerDie: write"); } + 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(); - rts_evalIO(cap,&base_GHCziConc_ensureIOManagerIsRunning_closure,NULL); + cap = ioManagerStartCap(cap); rts_unlock(cap); } } @@ -158,8 +169,8 @@ 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 */ @@ -171,18 +182,31 @@ StgPtr *next_pending_handler = pending_handler_buf; * -------------------------------------------------------------------------- */ 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 @@ -218,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]) { @@ -226,14 +252,9 @@ generic_handler(int sig) stg_exit(EXIT_FAILURE); } -#endif /* THREADED_RTS */ + contextSwitchCapability(&MainCapability); - // re-establish the signal handler, and carry on - sigemptyset(&signals); - sigaddset(&signals, sig); - sigprocmask(SIG_UNBLOCK, &signals, NULL); - - context_switch = 1; +#endif /* THREADED_RTS */ } /* ----------------------------------------------------------------------------- @@ -247,6 +268,10 @@ void initUserSignals(void) { sigemptyset(&userSignals); +#ifndef THREADED_RTS + getStablePtr((StgPtr)&base_GHCziConcziSignal_runHandlers_closure); + // needed to keep runHandler alive +#endif } void @@ -279,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; @@ -303,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: @@ -336,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; } /* ----------------------------------------------------------------------------- @@ -365,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(); @@ -383,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, void *user) -{ - StgPtr *p; - - p = next_pending_handler; - while (p != pending_handler_buf) { - p--; - evac(user, (StgClosure **)p); - } -} -#else void 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"); @@ -443,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 @@ -498,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 @@ -523,6 +637,8 @@ resetDefaultHandlers(void) if (sigaction(SIGPIPE, &action, NULL) != 0) { sysErrorBelch("warning: failed to uninstall SIGPIPE handler"); } + + set_sigtstp_action(rtsFalse); } void