summaryrefslogtreecommitdiff
path: root/rts/win32/Ticker.c
blob: 1c4548265118521b54bff02ea5eef391ebc95dca (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
/*
 * RTS periodic timers.
 * 
 */
#include "Rts.h"
#include "Ticker.h"
#include <windows.h>
#include <stdio.h>
#include <process.h>

/*
 * Provide a timer service for the RTS, periodically
 * notifying it that a number of 'ticks' has passed.
 *
 */

/* 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.
 *
 * This is the portable way of providing a timer service under
 * Win32; features like waitable timers or timer queues are only
 * supported by a subset of the Win32 platforms (notably not
 * under Win9x.)
 *
 */
static
unsigned
WINAPI
TimerProc(PVOID param)
{
  int ms = (int)param;
  DWORD waitRes = 0;
  
  /* interpret a < 0 timeout period as 'instantaneous' */ 
  if (ms < 0) ms = 0;

  while (1) {
      switch (ticker_state) {
      case TickerGo:
          waitRes = WaitForSingleObject(hStopEvent, ms);
          break;
      case TickerPause:
          waitRes = WaitForSingleObject(hStopEvent, INFINITE);
          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
initTicker (nat ms, TickProc handle_tick)
{
  unsigned threadId;
  /* 'hStopEvent' is a manual-reset event that's signalled upon
   * shutdown of timer service (=> timer thread.)
   */
  hStopEvent = CreateEvent ( NULL,
			     TRUE,
			     FALSE,
			     NULL);
  if (hStopEvent == INVALID_HANDLE_VALUE) {
      sysErrorBelch("CreateEvent");
      stg_exit(EXIT_FAILURE);
  }
  tickProc = handle_tick;
  ticker_state = TickerPause;
  tickThread = (HANDLE)(long)_beginthreadex( NULL,
			       0,
			       TimerProc,
			       (LPVOID)ms,
			       0,
			       &threadId);

  if (tickThread == 0) {
      sysErrorBelch("_beginthreadex");
      stg_exit(EXIT_FAILURE);
  }
}

void
startTicker(void)
{
    ticker_state = TickerGo;
    SetEvent(hStopEvent);
}

void
stopTicker(void)
{
    ticker_state = TickerPause;
    SetEvent(hStopEvent);
}

void
exitTicker (rtsBool wait)
{
    // 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.

    if (hStopEvent != INVALID_HANDLE_VALUE && 
	tickThread != INVALID_HANDLE_VALUE) {
	DWORD exitCode;
        ticker_state = TickerExit;
	SetEvent(hStopEvent);
	while (wait) {
            // See #3748:
            //
            // when the RTS is compiled into a DLL (wait==rtsTrue),
            // the ticker thread must stop before we exit, or chaos
            // will ensue.  We can't kill it, because it may be
            // holding a lock.
            //
            // When not compiled into a DLL, we wait for
            // the thread out of courtesy, but give up after 200ms if
            // it still hasn't stopped.
	    WaitForSingleObject(tickThread, 200);
	    if (!GetExitCodeThread(tickThread, &exitCode)) {
		return;
	    }
            CloseHandle(tickThread);
            if (exitCode != STILL_ACTIVE) {
		tickThread = INVALID_HANDLE_VALUE;
		if ( hStopEvent != INVALID_HANDLE_VALUE ) {
		    CloseHandle(hStopEvent);
		    hStopEvent = INVALID_HANDLE_VALUE;
		}
		return;
	    }
	}
    }
}