Document CoreSyn and expand its API
[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           waitRes = WaitForSingleObject(hStopEvent, INFINITE);
57           break;
58       case TickerExit:
59           /* event has become signalled */
60           tickProc = NULL;
61           CloseHandle(hStopEvent);
62           hStopEvent = INVALID_HANDLE_VALUE;
63           return 0;
64       }
65       
66       switch (waitRes) {
67       case WAIT_OBJECT_0:
68           /* event has become signalled */
69           ResetEvent(hStopEvent);
70           continue;
71       case WAIT_TIMEOUT:
72           /* tick */
73           tickProc(0);
74           break;
75       case WAIT_FAILED:
76           sysErrorBelch("TimerProc: WaitForSingleObject failed");
77           break; 
78       default:
79           errorBelch("TimerProc: unexpected result %lu\n", waitRes);
80           break;
81       }
82   }
83   return 0;
84 }
85
86
87 void
88 initTicker (nat ms, TickProc handle_tick)
89 {
90   unsigned threadId;
91   /* 'hStopEvent' is a manual-reset event that's signalled upon
92    * shutdown of timer service (=> timer thread.)
93    */
94   hStopEvent = CreateEvent ( NULL,
95                              TRUE,
96                              FALSE,
97                              NULL);
98   if (hStopEvent == INVALID_HANDLE_VALUE) {
99       sysErrorBelch("CreateEvent");
100       stg_exit(EXIT_FAILURE);
101   }
102   tickProc = handle_tick;
103   ticker_state = TickerPause;
104   tickThread = (HANDLE)(long)_beginthreadex( NULL,
105                                0,
106                                TimerProc,
107                                (LPVOID)ms,
108                                0,
109                                &threadId);
110
111   if (tickThread == 0) {
112       sysErrorBelch("_beginthreadex");
113       stg_exit(EXIT_FAILURE);
114   }
115 }
116
117 void
118 startTicker(void)
119 {
120     ticker_state = TickerGo;
121     SetEvent(hStopEvent);
122 }
123
124 void
125 stopTicker(void)
126 {
127     ticker_state = TickerPause;
128     SetEvent(hStopEvent);
129 }
130
131 void
132 exitTicker(void)
133 {
134     // We must wait for the ticker thread to terminate, since if we
135     // are in a DLL that is about to be unloaded, the ticker thread
136     // cannot be allowed to return to a missing DLL.
137
138     if (hStopEvent != INVALID_HANDLE_VALUE && 
139         tickThread != INVALID_HANDLE_VALUE) {
140         DWORD exitCode;
141         ticker_state = TickerExit;
142         SetEvent(hStopEvent);
143         while (1) {
144             WaitForSingleObject(tickThread, 20);
145             if (!GetExitCodeThread(tickThread, &exitCode)) {
146                 return 1;
147             }
148             if (exitCode != STILL_ACTIVE) {
149                 tickThread = INVALID_HANDLE_VALUE;
150                 if ( hStopEvent != INVALID_HANDLE_VALUE ) {
151                     CloseHandle(hStopEvent);
152                     hStopEvent = INVALID_HANDLE_VALUE;
153                 }
154                 return 0;
155             }
156             TerminateThread(tickThread, 0);
157         }
158     }
159 }