diff options
author | Andreas Voellmy <andreas.voellmy@gmail.com> | 2014-08-19 08:02:18 -0500 |
---|---|---|
committer | Austin Seipp <austin@well-typed.com> | 2014-08-19 08:03:34 -0500 |
commit | f9f89b7884ccc8ee5047cf4fffdf2b36df6832df (patch) | |
tree | 2871a2b4bca35ed8f2cecdb71d72ce66885a2c54 /rts/posix/Signals.c | |
parent | 51a0b60038fb1dddbb9793c0c069de6125f84bf1 (diff) | |
download | haskell-f9f89b7884ccc8ee5047cf4fffdf2b36df6832df.tar.gz |
rts/base: Fix #9423
Summary:
Fix #9423.
The problem in #9423 is caused when code invoked by `hs_exit()` waits
on all foreign calls to return, but some IO managers are in `safe` foreign
calls and do not return. The previous design signaled to the timer manager
(via its control pipe) that it should "die" and when the timer manager
returned to Haskell-land, the Haskell code in timer manager then signalled
to the IO manager threads that they should return from foreign calls and
`die`. Unfortunately, in the shutdown sequence the timer manager is unable
to return to Haskell-land fast enough and so the code that signals to the
IO manager threads (via their control pipes) is never executed and the IO
manager threads remain out in the foreign calls.
This patch solves this problem by having the RTS signal to all the IO
manager threads (via their control pipes; and in addition to signalling
to the timer manager thread) that they should shutdown (in `ioManagerDie()`
in `rts/Signals.c`. To do this, we arrange for each IO manager thread to
register its control pipe with the RTS (in `GHC.Thread.startIOManagerThread`).
In addition, `GHC.Thread.startTimerManagerThread` registers its control pipe.
These are registered via C functions `setTimerManagerControlFd` (in
`rts/Signals.c`) and `setIOManagerControlFd` (in `rts/Capability.c`). The IO
manager control pipe file descriptors are stored in a new field of the
`Capability_ struct`.
Test Plan: See the notes on #9423 to recreate the problem and to verify that it no longer occurs with the fix.
Auditors: simonmar
Reviewers: simonmar, edsko, ezyang, austin
Reviewed By: austin
Subscribers: phaskell, simonmar, ezyang, carter, relrod
Differential Revision: https://phabricator.haskell.org/D129
GHC Trac Issues: #9423, #9284
Diffstat (limited to 'rts/posix/Signals.c')
-rw-r--r-- | rts/posix/Signals.c | 80 |
1 files changed, 50 insertions, 30 deletions
diff --git a/rts/posix/Signals.c b/rts/posix/Signals.c index d5129f0996..ba4a8b75ea 100644 --- a/rts/posix/Signals.c +++ b/rts/posix/Signals.c @@ -127,28 +127,27 @@ more_handlers(int sig) // Here's the pipe into which we will send our signals static int io_manager_wakeup_fd = -1; -static int io_manager_control_fd = -1; +static int timer_manager_control_wr_fd = -1; #define IO_MANAGER_WAKEUP 0xff #define IO_MANAGER_DIE 0xfe #define IO_MANAGER_SYNC 0xfd -void -setIOManagerWakeupFd (int fd) -{ - // only called when THREADED_RTS, but unconditionally - // compiled here because GHC.Event.Control depends on it. - io_manager_wakeup_fd = fd; +void setTimerManagerControlFd(int fd) { + timer_manager_control_wr_fd = fd; } void -setIOManagerControlFd (int fd) +setIOManagerWakeupFd (int fd) { // only called when THREADED_RTS, but unconditionally // compiled here because GHC.Event.Control depends on it. - io_manager_control_fd = fd; + io_manager_wakeup_fd = fd; } +/* ----------------------------------------------------------------------------- + * Wake up at least one IO or timer manager HS thread. + * -------------------------------------------------------------------------- */ void ioManagerWakeup (void) { @@ -170,14 +169,24 @@ ioManagerWakeup (void) void ioManagerDie (void) { + StgWord8 byte = (StgWord8)IO_MANAGER_DIE; + nat i; + int fd; 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 (0 <= timer_manager_control_wr_fd) { + r = write(timer_manager_control_wr_fd, &byte, 1); if (r == -1) { sysErrorBelch("ioManagerDie: write"); } - io_manager_control_fd = -1; - io_manager_wakeup_fd = -1; + timer_manager_control_wr_fd = -1; + } + + for (i=0; i < n_capabilities; i++) { + fd = capabilities[i]->io_manager_control_wr_fd; + if (0 <= fd) { + r = write(fd, &byte, 1); + if (r == -1) { sysErrorBelch("ioManagerDie: write"); } + capabilities[i]->io_manager_control_wr_fd = -1; + } } } @@ -192,7 +201,7 @@ ioManagerStart (void) { // Make sure the IO manager thread is running Capability *cap; - if (io_manager_control_fd < 0 || io_manager_wakeup_fd < 0) { + if (timer_manager_control_wr_fd < 0 || io_manager_wakeup_fd < 0) { cap = rts_lock(); ioManagerStartCap(&cap); rts_unlock(cap); @@ -223,26 +232,37 @@ generic_handler(int sig USED_IF_THREADS, { #if defined(THREADED_RTS) - if (io_manager_control_fd != -1) - { - StgWord8 buf[sizeof(siginfo_t) + 1]; - int r; + StgWord8 buf[sizeof(siginfo_t) + 1]; + int r; - buf[0] = sig; + 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)); + } - 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)); + if (0 <= timer_manager_control_wr_fd) + { + r = write(timer_manager_control_wr_fd, buf, sizeof(siginfo_t)+1); + if (r == -1 && errno == EAGAIN) { + errorBelch("lost signal due to full pipe: %d\n", sig); } + } - 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); + nat i; + int fd; + for (i=0; i < n_capabilities; i++) { + fd = capabilities[i]->io_manager_control_wr_fd; + if (0 <= fd) { + r = write(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 // the signal.. |