FIX #1623: disable the timer signal when the system is idle (threaded RTS only)
[ghc-hetmet.git] / rts / win32 / Ticker.c
1 /*
2  * RTS periodic timers.
3  * 
4  */
5 #include "Rts.h"
6 #include "Timer.h"
7 #include "Ticker.h"
8 #include <windows.h>
9 #include <stdio.h>
10 #include <process.h>
11 #include "OSThreads.h"
12
13 /*
14  * Provide a timer service for the RTS, periodically
15  * notifying it that a number of 'ticks' has passed.
16  *
17  */
18
19 /* To signal pause or shutdown of the timer service, we use a local
20  * event which the timer thread listens to.
21  */
22 static HANDLE hStopEvent = INVALID_HANDLE_VALUE;
23 static HANDLE tickThread = INVALID_HANDLE_VALUE;
24
25 static TickProc tickProc = NULL;
26
27 static enum { TickerGo, TickerPause, TickerExit } ticker_state;
28
29 /*
30  * Ticking is done by a separate thread which periodically
31  * wakes up to handle a tick.
32  *
33  * This is the portable way of providing a timer service under
34  * Win32; features like waitable timers or timer queues are only
35  * supported by a subset of the Win32 platforms (notably not
36  * under Win9x.)
37  *
38  */
39 static
40 unsigned
41 WINAPI
42 TimerProc(PVOID param)
43 {
44   int ms = (int)param;
45   DWORD waitRes;
46   
47   /* interpret a < 0 timeout period as 'instantaneous' */ 
48   if (ms < 0) ms = 0;
49
50   while (1) {
51       switch (ticker_state) {
52       case TickerGo:
53           waitRes = WaitForSingleObject(hStopEvent, ms);
54           break;
55       case TickerPause:
56           debugBelch("tick: pause");
57           waitRes = WaitForSingleObject(hStopEvent, INFINITE);
58           debugBelch("tick: wakeup");
59           break;
60       case TickerExit:
61           /* event has become signalled */
62           tickProc = NULL;
63           CloseHandle(hStopEvent);
64           hStopEvent = INVALID_HANDLE_VALUE;
65           return 0;
66       }
67       
68       switch (waitRes) {
69       case WAIT_OBJECT_0:
70           /* event has become signalled */
71           ResetEvent(hStopEvent);
72           continue;
73       case WAIT_TIMEOUT:
74           /* tick */
75           tickProc(0);
76           break;
77       case WAIT_FAILED:
78           sysErrorBelch("TimerProc: WaitForSingleObject failed");
79           break; 
80       default:
81           errorBelch("TimerProc: unexpected result %lu\n", waitRes);
82           break;
83       }
84   }
85   return 0;
86 }
87
88
89 void
90 initTicker (nat ms, TickProc handle_tick)
91 {
92   unsigned threadId;
93   /* 'hStopEvent' is a manual-reset event that's signalled upon
94    * shutdown of timer service (=> timer thread.)
95    */
96   hStopEvent = CreateEvent ( NULL,
97                              TRUE,
98                              FALSE,
99                              NULL);
100   if (hStopEvent == INVALID_HANDLE_VALUE) {
101       sysErrorBelch("CreateEvent");
102       stg_exit(EXIT_FAILURE);
103   }
104   tickProc = handle_tick;
105   ticker_state = TickerPause;
106   tickThread = (HANDLE)(long)_beginthreadex( NULL,
107                                0,
108                                TimerProc,
109                                (LPVOID)ms,
110                                0,
111                                &threadId);
112
113   if (tickThread == 0) {
114       sysErrorBelch("_beginthreadex");
115       stg_exit(EXIT_FAILURE);
116   }
117 }
118
119 void
120 startTicker(void)
121 {
122     ticker_state = TickerGo;
123     SetEvent(hStopEvent);
124 }
125
126 void
127 stopTicker(void)
128 {
129     ticker_state = TickerPause;
130     SetEvent(hStopEvent);
131 }
132
133 void
134 exitTicker(void)
135 {
136     // We must wait for the ticker thread to terminate, since if we
137     // are in a DLL that is about to be unloaded, the ticker thread
138     // cannot be allowed to return to a missing DLL.
139
140     if (hStopEvent != INVALID_HANDLE_VALUE && 
141         tickThread != INVALID_HANDLE_VALUE) {
142         DWORD exitCode;
143         ticker_state = TickerExit;
144         SetEvent(hStopEvent);
145         while (1) {
146             WaitForSingleObject(tickThread, 20);
147             if (!GetExitCodeThread(tickThread, &exitCode)) {
148                 return 1;
149             }
150             if (exitCode != STILL_ACTIVE) {
151                 tickThread = INVALID_HANDLE_VALUE;
152                 if ( hStopEvent != INVALID_HANDLE_VALUE ) {
153                     CloseHandle(hStopEvent);
154                     hStopEvent = INVALID_HANDLE_VALUE;
155                 }
156                 return 0;
157             }
158             TerminateThread(tickThread, 0);
159         }
160     }
161 }