summaryrefslogtreecommitdiff
path: root/rts
diff options
context:
space:
mode:
authorBen Gamari <bgamari.foss@gmail.com>2016-05-01 13:38:38 +0200
committerBen Gamari <ben@smart-cactus.org>2016-05-01 23:29:49 +0200
commit16a51a6c2f265f8670355be03d42b773d93e0684 (patch)
treedf934df75e6fc877ff28925167687d563cbc5533 /rts
parent533037cc58a7c50e1c014e27e8b971d53e7b47bd (diff)
downloadhaskell-16a51a6c2f265f8670355be03d42b773d93e0684.tar.gz
rts: Close livelock window due to rapid ticker enable/disable
This fixes #11830, where the RTS would livelock if run with `-I0` due to a regression introduced by bbdc52f3a6e6a28e209fb8f65699121d4ef3a4e3. The reason for this is that the new codepath introduced a subtle race condition: 1. one thread could request that the ticker stop and would block until the ticker in fact stopped 2. meanwhile, another thread could sneak in and restart the ticker this was implemented in such a way where thread (1) would end up blocked forever. The solution here is to simply not block. The worst that will happen is that timer fires again, but is ignored since the ticker is stopped. Test Plan: Validate, try reproduction case in #11830. Need to find a nice testcase. Reviewers: simonmar, erikd, hsyl20, austin Reviewed By: erikd, hsyl20 Subscribers: erikd, thomie Differential Revision: https://phabricator.haskell.org/D2129 GHC Trac Issues: #11830
Diffstat (limited to 'rts')
-rw-r--r--rts/posix/Itimer.c16
1 files changed, 10 insertions, 6 deletions
diff --git a/rts/posix/Itimer.c b/rts/posix/Itimer.c
index 8915446814..770ff202ea 100644
--- a/rts/posix/Itimer.c
+++ b/rts/posix/Itimer.c
@@ -177,6 +177,7 @@ static void install_vtalrm_handler(TickProc handle_tick)
#if defined(USE_PTHREAD_FOR_ITIMER)
enum ItimerState {STOPPED, RUNNING, STOPPING, EXITED};
static volatile enum ItimerState itimer_state = STOPPED;
+
static void *itimer_thread_func(void *_handle_tick)
{
TickProc handle_tick = _handle_tick;
@@ -189,7 +190,7 @@ static void *itimer_thread_func(void *_handle_tick)
it.it_value.tv_nsec = TimeToNS(itimer_interval) % 1000000000;
it.it_interval = it.it_value;
- timerfd = timerfd_create(CLOCK_MONOTONIC,TFD_CLOEXEC);
+ timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
if (timerfd == -1) {
sysErrorBelch("timerfd_create");
stg_exit(EXIT_FAILURE);
@@ -197,7 +198,7 @@ static void *itimer_thread_func(void *_handle_tick)
if (!TFD_CLOEXEC) {
fcntl(timerfd, F_SETFD, FD_CLOEXEC);
}
- timerfd_settime(timerfd,0,&it,NULL);
+ int ret = timerfd_settime(timerfd, 0, &it, NULL);
#endif
while (1) {
@@ -270,6 +271,11 @@ void
startTicker(void)
{
#if defined(USE_PTHREAD_FOR_ITIMER)
+ // sanity check
+ if (itimer_state == EXITED) {
+ sysErrorBelch("ITimer: Tried to start a dead timer!\n");
+ stg_exit(EXIT_FAILURE);
+ }
itimer_state = RUNNING;
#elif defined(USE_TIMER_CREATE)
{
@@ -306,10 +312,8 @@ stopTicker(void)
#if defined(USE_PTHREAD_FOR_ITIMER)
if (itimer_state == RUNNING) {
itimer_state = STOPPING;
- /* Wait for the thread to confirm it won't generate another tick. */
- write_barrier();
- while (itimer_state != STOPPED)
- sched_yield();
+ // Note that the timer may fire once more, but that's okay;
+ // handle_tick is only called when itimer_state == RUNNING
}
#elif defined(USE_TIMER_CREATE)
struct itimerspec it;