diff options
-rw-r--r-- | rts/RtsStartup.c | 2 | ||||
-rw-r--r-- | rts/Schedule.c | 18 | ||||
-rw-r--r-- | rts/Ticker.h | 10 | ||||
-rw-r--r-- | rts/Timer.c | 52 | ||||
-rw-r--r-- | rts/Timer.h | 6 | ||||
-rw-r--r-- | rts/posix/Itimer.c | 30 | ||||
-rw-r--r-- | rts/win32/Ticker.c | 85 |
7 files changed, 146 insertions, 57 deletions
diff --git a/rts/RtsStartup.c b/rts/RtsStartup.c index 51047218fe..cdb45c60f2 100644 --- a/rts/RtsStartup.c +++ b/rts/RtsStartup.c @@ -246,6 +246,7 @@ hs_init(int *argc, char **argv[]) initProfiling1(); /* start the virtual timer 'subsystem'. */ + initTimer(); startTimer(); /* Initialise the stats department */ @@ -409,6 +410,7 @@ hs_exit_(rtsBool wait_foreign) /* stop the ticker */ stopTimer(); + exitTimer(); /* reset the standard file descriptors to blocking mode */ resetNonBlockingFd(0); diff --git a/rts/Schedule.c b/rts/Schedule.c index 8bd42414b5..b68e1ac88f 100644 --- a/rts/Schedule.c +++ b/rts/Schedule.c @@ -593,7 +593,19 @@ run_thread: dirtyTSO(t); - recent_activity = ACTIVITY_YES; +#if defined(THREADED_RTS) + if (recent_activity == ACTIVITY_DONE_GC) { + // ACTIVITY_DONE_GC means we turned off the timer signal to + // conserve power (see #1623). Re-enable it here. + nat prev; + prev = xchg(&recent_activity, ACTIVITY_YES); + if (prev == ACTIVITY_DONE_GC) { + startTimer(); + } + } else { + recent_activity = ACTIVITY_YES; + } +#endif switch (prev_what_next) { @@ -974,6 +986,8 @@ scheduleDetectDeadlock (Capability *cap, Task *task) cap = scheduleDoGC (cap, task, rtsTrue/*force major GC*/); recent_activity = ACTIVITY_DONE_GC; + // disable timer signals (see #1623) + stopTimer(); if ( !emptyRunQueue(cap) ) return; @@ -2185,6 +2199,7 @@ forkProcess(HsStablePtr *entry // On Unix, all timers are reset in the child, so we need to start // the timer again. + initTimer(); startTimer(); cap = rts_evalStableIO(cap, entry, NULL); // run the action @@ -2531,6 +2546,7 @@ initScheduler(void) context_switch = 0; sched_state = SCHED_RUNNING; + recent_activity = ACTIVITY_YES; #if defined(THREADED_RTS) /* Initialise the mutex and condition variables used by diff --git a/rts/Ticker.h b/rts/Ticker.h index b06890a049..a39e7d69c1 100644 --- a/rts/Ticker.h +++ b/rts/Ticker.h @@ -2,14 +2,18 @@ * * (c) The GHC Team 2005 * - * Ticker interface (implementation is OS-specific) + * Interface to the OS-specific implementation of a regular time signal. * * ---------------------------------------------------------------------------*/ #ifndef TICKER_H #define TICKER_H -extern void startTicker( nat ms, TickProc handle_tick ); -extern void stopTicker ( void ); +typedef void (*TickProc)(int); + +extern void initTicker (nat ms, TickProc handle_tick); +extern void startTicker (void); +extern void stopTicker (void); +extern void exitTicker (void); #endif /* TICKER_H */ diff --git a/rts/Timer.c b/rts/Timer.c index 586991a4db..9822239b33 100644 --- a/rts/Timer.c +++ b/rts/Timer.c @@ -64,15 +64,21 @@ handle_tick(int unused STG_UNUSED) RtsFlags.MiscFlags.tickInterval; break; case ACTIVITY_MAYBE_NO: - if (ticks_to_gc == 0) break; /* 0 ==> no idle GC */ - ticks_to_gc--; if (ticks_to_gc == 0) { - ticks_to_gc = RtsFlags.GcFlags.idleGCDelayTime / - RtsFlags.MiscFlags.tickInterval; - recent_activity = ACTIVITY_INACTIVE; - blackholes_need_checking = rtsTrue; - /* hack: re-use the blackholes_need_checking flag */ - wakeUpRts(); + /* 0 ==> no idle GC */ + recent_activity = ACTIVITY_DONE_GC; + // disable timer signals (see #1623) + stopTimer(); + } else { + ticks_to_gc--; + if (ticks_to_gc == 0) { + ticks_to_gc = RtsFlags.GcFlags.idleGCDelayTime / + RtsFlags.MiscFlags.tickInterval; + recent_activity = ACTIVITY_INACTIVE; + blackholes_need_checking = rtsTrue; + /* hack: re-use the blackholes_need_checking flag */ + wakeUpRts(); + } } break; default: @@ -82,18 +88,34 @@ handle_tick(int unused STG_UNUSED) } void +initTimer(void) +{ + initProfTimer(); + if (RtsFlags.MiscFlags.tickInterval != 0) { + initTicker(RtsFlags.MiscFlags.tickInterval, handle_tick); + } +} + +void startTimer(void) { - initProfTimer(); - if (RtsFlags.MiscFlags.tickInterval != 0) { - startTicker(RtsFlags.MiscFlags.tickInterval, handle_tick); - } + if (RtsFlags.MiscFlags.tickInterval != 0) { + startTicker(); + } } void stopTimer(void) { - if (RtsFlags.MiscFlags.tickInterval != 0) { - stopTicker(); - } + if (RtsFlags.MiscFlags.tickInterval != 0) { + stopTicker(); + } +} + +void +exitTimer(void) +{ + if (RtsFlags.MiscFlags.tickInterval != 0) { + exitTicker(); + } } diff --git a/rts/Timer.h b/rts/Timer.h index 6d3c4150b0..59b695cac2 100644 --- a/rts/Timer.h +++ b/rts/Timer.h @@ -2,16 +2,16 @@ * * (c) The GHC Team, 1995-2006 * - * Interval timer service for profiling and pre-emptive scheduling. + * Interface to the RTS timer signal (uses OS-dependent Ticker.h underneath) * * ---------------------------------------------------------------------------*/ #ifndef TIMER_H #define TIMER_H -typedef void (*TickProc)(int); - +extern void initTimer(void); extern void startTimer(void); extern void stopTimer(void); +extern void exitTimer(void); #endif /* TIMER_H */ diff --git a/rts/posix/Itimer.c b/rts/posix/Itimer.c index df95f21b92..51e08f8280 100644 --- a/rts/posix/Itimer.c +++ b/rts/posix/Itimer.c @@ -127,7 +127,7 @@ install_vtalrm_handler(TickProc handle_tick) } void -startTicker(nat ms, TickProc handle_tick) +initTicker (TickProc handle_tick) { install_vtalrm_handler(handle_tick); @@ -137,21 +137,30 @@ startTicker(nat ms, TickProc handle_tick) #if defined(USE_TIMER_CREATE) { - struct itimerspec it; struct sigevent ev; ev.sigev_notify = SIGEV_SIGNAL; ev.sigev_signo = ITIMER_SIGNAL; - - it.it_value.tv_sec = ms / 1000; - it.it_value.tv_nsec = (ms % 1000) * 1000000; - it.it_interval = it.it_value; - + if (timer_create(TIMER_FLAVOUR, &ev, &timer) != 0) { sysErrorBelch("timer_create"); stg_exit(EXIT_FAILURE); } + } +#endif +} +void +startTicker(nat ms) +{ +#if defined(USE_TIMER_CREATE) + { + struct itimerspec it; + + it.it_value.tv_sec = ms / 1000; + it.it_value.tv_nsec = (ms % 1000) * 1000000; + it.it_interval = it.it_value; + if (timer_settime(timer, 0, &it, NULL) != 0) { sysErrorBelch("timer_settime"); stg_exit(EXIT_FAILURE); @@ -201,6 +210,13 @@ stopTicker(void) #endif } +void +exitTicker(void) +{ + timer_delete(timer); + // ignore errors - we don't really care if it fails. +} + #if 0 /* Currently unused */ void diff --git a/rts/win32/Ticker.c b/rts/win32/Ticker.c index 5b41494d47..d425dd58ab 100644 --- a/rts/win32/Ticker.c +++ b/rts/win32/Ticker.c @@ -16,15 +16,16 @@ * */ -/* To signal shutdown of the timer service, we use a local - * event which the timer thread listens to (and stopVirtTimer() - * signals.) +/* To signal pause or shutdown of the timer service, we use a local + * event which the timer thread listens to. */ static HANDLE hStopEvent = INVALID_HANDLE_VALUE; static HANDLE tickThread = INVALID_HANDLE_VALUE; static TickProc tickProc = NULL; +static enum { TickerGo, TickerPause, TickerExit } ticker_state; + /* * Ticking is done by a separate thread which periodically * wakes up to handle a tick. @@ -44,38 +45,49 @@ TimerProc(PVOID param) DWORD waitRes; /* interpret a < 0 timeout period as 'instantaneous' */ - if (ms < 0) ms = 0; + if (ms < 0) ms = 0; while (1) { - waitRes = WaitForSingleObject(hStopEvent, ms); - - switch (waitRes) { - case WAIT_OBJECT_0: - /* event has become signalled */ - tickProc = NULL; - CloseHandle(hStopEvent); - hStopEvent = INVALID_HANDLE_VALUE; - return 0; - case WAIT_TIMEOUT: - /* tick */ - tickProc(0); - break; - case WAIT_FAILED: { - DWORD dw = GetLastError(); - fprintf(stderr, "TimerProc: wait failed -- error code: %lu\n", dw); fflush(stderr); - break; - } - default: - fprintf(stderr, "TimerProc: unexpected result %lu\n", waitRes); fflush(stderr); - break; - } + switch (ticker_state) { + case TickerGo: + waitRes = WaitForSingleObject(hStopEvent, ms); + break; + case TickerPause: + debugBelch("tick: pause"); + waitRes = WaitForSingleObject(hStopEvent, INFINITE); + debugBelch("tick: wakeup"); + break; + case TickerExit: + /* event has become signalled */ + tickProc = NULL; + CloseHandle(hStopEvent); + hStopEvent = INVALID_HANDLE_VALUE; + return 0; + } + + switch (waitRes) { + case WAIT_OBJECT_0: + /* event has become signalled */ + ResetEvent(hStopEvent); + continue; + case WAIT_TIMEOUT: + /* tick */ + tickProc(0); + break; + case WAIT_FAILED: + sysErrorBelch("TimerProc: WaitForSingleObject failed"); + break; + default: + errorBelch("TimerProc: unexpected result %lu\n", waitRes); + break; + } } return 0; } void -startTicker(nat ms, TickProc handle_tick) +initTicker (nat ms, TickProc handle_tick) { unsigned threadId; /* 'hStopEvent' is a manual-reset event that's signalled upon @@ -86,9 +98,11 @@ startTicker(nat ms, TickProc handle_tick) FALSE, NULL); if (hStopEvent == INVALID_HANDLE_VALUE) { - return 0; + sysErrorBelch("CreateEvent"); + stg_exit(EXIT_FAILURE); } tickProc = handle_tick; + ticker_state = TickerPause; tickThread = (HANDLE)(long)_beginthreadex( NULL, 0, TimerProc, @@ -103,8 +117,22 @@ startTicker(nat ms, TickProc handle_tick) } void +startTicker(void) +{ + ticker_state = TickerGo; + SetEvent(hStopEvent); +} + +void stopTicker(void) { + ticker_state = TickerPause; + SetEvent(hStopEvent); +} + +void +exitTicker(void) +{ // We must wait for the ticker thread to terminate, since if we // are in a DLL that is about to be unloaded, the ticker thread // cannot be allowed to return to a missing DLL. @@ -112,6 +140,7 @@ stopTicker(void) if (hStopEvent != INVALID_HANDLE_VALUE && tickThread != INVALID_HANDLE_VALUE) { DWORD exitCode; + ticker_state = TickerExit; SetEvent(hStopEvent); while (1) { WaitForSingleObject(tickThread, 20); |