Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
whrt.cpp
Go to the documentation of this file.
1 /* Copyright (c) 2010 Wildfire Games
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining
4  * a copy of this software and associated documentation files (the
5  * "Software"), to deal in the Software without restriction, including
6  * without limitation the rights to use, copy, modify, merge, publish,
7  * distribute, sublicense, and/or sell copies of the Software, and to
8  * permit persons to whom the Software is furnished to do so, subject to
9  * the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included
12  * in all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  */
22 
23 /*
24  * Windows High Resolution Timer
25  */
26 
27 #include "precompiled.h"
29 
30 #include <process.h> // _beginthreadex
31 
32 #include "lib/sysdep/cpu.h"
35 #include "lib/sysdep/acpi.h"
36 #include "lib/bits.h"
37 
39 
40 WINIT_REGISTER_EARLY_INIT2(whrt_Init); // wutil -> whrt -> wtime
42 
43 
44 namespace ERR
45 {
46  const Status WHRT_COUNTER_UNSAFE = 140000;
47 }
48 
49 
50 //-----------------------------------------------------------------------------
51 // choose best available safe counter
52 
53 // (moved into a separate function to simplify error handling)
55 {
56  RETURN_STATUS_IF_ERR(counter->Activate());
57 
58  if(!counter->IsSafe())
59  return ERR::WHRT_COUNTER_UNSAFE; // NOWARN (happens often)
60 
61  return INFO::OK;
62 }
63 
64 /**
65  * @return the newly created and unique instance of the next best counter
66  * that is deemed safe, or 0 if all have already been created.
67  **/
69 {
70  for(;;)
71  {
72  static size_t nextCounterId = 0;
73  ICounter* counter = CreateCounter(nextCounterId++);
74  if(!counter)
75  return 0; // tried all, none were safe
76 
77  Status err = ActivateCounter(counter);
78  if(err == INFO::OK)
79  {
80  debug_printf(L"HRT: using name=%ls freq=%f\n", counter->Name(), counter->NominalFrequency());
81  return counter; // found a safe counter
82  }
83  else
84  {
85  wchar_t buf[100];
86  debug_printf(L"HRT: activating %ls failed: %ls\n", counter->Name(), StatusDescription(err, buf, ARRAY_SIZE(buf)));
87  DestroyCounter(counter);
88  }
89  }
90 }
91 
92 
93 //-----------------------------------------------------------------------------
94 // counter that drives the timer
95 
96 static ICounter* counter;
97 // (these counter properties are cached for efficiency and convenience:)
98 static double nominalFrequency;
99 static double resolution;
100 static size_t counterBits;
102 
103 static void InitCounter()
104 {
105  // we used to support switching counters at runtime, but that's
106  // unnecessarily complex. it need and should only be done once.
107  ENSURE(counter == 0);
108  counter = GetNextBestSafeCounter();
109  ENSURE(counter != 0);
110 
111  nominalFrequency = counter->NominalFrequency();
112  resolution = counter->Resolution();
113  counterBits = counter->CounterBits();
114  debug_printf(L"HRT: counter=%ls freq=%g res=%g bits=%d\n", counter->Name(), nominalFrequency, resolution, counterBits);
115 
116  // sanity checks
117  ENSURE(nominalFrequency >= 500.0-DBL_EPSILON);
118  ENSURE(resolution <= 2e-3);
119  ENSURE(8 <= counterBits && counterBits <= 64);
120 
121  counterMask = bit_mask<u64>(counterBits);
122 }
123 
124 static void ShutdownCounter()
125 {
126  DestroyCounter(counter);
127 }
128 
129 static inline u64 Counter()
130 {
131  return counter->Counter();
132 }
133 
134 /**
135  * @return difference [ticks], taking rollover into account.
136  * (time-critical, so it's not called through ICounter.)
137  **/
138 static inline u64 CounterDelta(u64 oldCounter, u64 newCounter)
139 {
140  return (newCounter - oldCounter) & counterMask;
141 }
142 
144 {
145  ENSURE(resolution != 0.0);
146  return resolution;
147 }
148 
149 
150 //-----------------------------------------------------------------------------
151 // timer state
152 
153 // we're not going to bother calibrating the counter (i.e. measuring its
154 // current frequency by means of a second timer). rationale:
155 // - all counters except the TSC are stable and run at fixed frequencies;
156 // - it's not clear that any other HRT or the tick count would be useful
157 // as a stable time reference (if it were, we should be using it instead);
158 // - calibration would complicate the code (we'd have to make sure the
159 // secondary counter is safe and can co-exist with the primary).
160 
161 /**
162  * stores all timer state shared between readers and the update thread.
163  * (must be POD because it's used before static ctors run.)
164  **/
166 {
167  // value of the counter at last update.
169 
170  // total elapsed time [seconds] since first update.
171  // converted from tick deltas with the *then current* frequency
172  // (this enables calibration, which is currently not implemented,
173  // but leaving open the possibility costs nothing)
174  double time;
175 
176  u8 padding[48];
177 };
178 
179 // how do we detect when the old TimerState is no longer in use and can be
180 // freed? we use two static instances (avoids dynamic allocation headaches)
181 // and swap between them ('double-buffering'). it is assumed that all
182 // entered critical sections (the latching of TimerState fields) will have
183 // been exited before the next update comes around; if not, TimerState.time
184 // changes, the critical section notices and re-reads the new values.
185 static __declspec(align(64)) TimerState timerStates[2];
186 // note: exchanging pointers is easier than XORing an index.
187 static volatile TimerState* volatile ts = &timerStates[0];
188 static volatile TimerState* volatile ts2 = &timerStates[1];
189 
190 static void UpdateTimerState()
191 {
192  // how can we synchronize readers and the update thread? locks are
193  // preferably avoided since they're dangerous and can be slow. what we
194  // need to ensure is that TimerState doesn't change while another thread is
195  // accessing it. the first step is to linearize the update, i.e. have it
196  // appear to happen in an instant (done by building a new TimerState and
197  // having it go live by switching pointers). all that remains is to make
198  // reads of the state variables consistent, done by latching them all and
199  // retrying if an update came in the middle of this.
200 
201  const u64 counter = Counter();
202  const u64 deltaTicks = CounterDelta(ts->counter, counter);
203  ts2->counter = counter;
204  ts2->time = ts->time + deltaTicks/nominalFrequency;
205  ts = (volatile TimerState*)InterlockedExchangePointer((volatile PVOID*)&ts2, (PVOID)ts);
206 }
207 
208 double whrt_Time()
209 {
210  // latch timer state (counter and time must be from the same update)
211  const volatile TimerState* state = ts;
212  const double time = state->time;
213  const u64 counter = state->counter;
214 
215  const u64 deltaTicks = CounterDelta(counter, Counter());
216  return (time + deltaTicks/nominalFrequency);
217 }
218 
219 
220 //-----------------------------------------------------------------------------
221 // update thread
222 
223 // note: we used to discipline the HRT timestamp to the system time, so it
224 // was advantageous to trigger updates via WinMM event (thus reducing
225 // instances where we're called in the middle of a scheduler tick).
226 // since that's no longer relevant, we prefer using a thread, because that
227 // avoids the dependency on WinMM and its lengthy startup time.
228 
229 // rationale: (+ and - are reasons for longer and shorter lengths)
230 // + minimize CPU usage
231 // + ensure all threads currently using TimerState return from those
232 // functions before the next interval
233 // - avoid more than 1 counter rollover per interval (InitUpdateThread makes
234 // sure our interval is shorter than the current counter's rollover rate)
235 static const DWORD UPDATE_INTERVAL_MS = 1000;
236 
239 
240 static unsigned __stdcall UpdateThread(void* UNUSED(data))
241 {
242  debug_SetThreadName("whrt_UpdateThread");
243 
244  for(;;)
245  {
246  const DWORD ret = WaitForSingleObject(hExitEvent, UPDATE_INTERVAL_MS);
247  // owner terminated or wait failed or exit event signaled - exit thread
248  if(ret != WAIT_TIMEOUT)
249  break;
250 
252  }
253 
254  return 0;
255 }
256 
257 static inline Status InitUpdateThread()
258 {
259  WinScopedPreserveLastError s; // CreateEvent
260 
261  // make sure our interval isn't too long
262  // (counterBits can be 64 => Bit() would overflow => calculate period/2)
263  const double period_2 = Bit<u64>(counterBits-1) / nominalFrequency;
264  const size_t rolloversPerInterval = size_t(UPDATE_INTERVAL_MS / i64(period_2*2.0*1000.0));
265  ENSURE(rolloversPerInterval <= 1);
266 
267  hExitEvent = CreateEvent(0, TRUE, FALSE, 0); // manual reset, initially false
268  if(hExitEvent == INVALID_HANDLE_VALUE)
270 
271  hUpdateThread = (HANDLE)_beginthreadex(0, 0, UpdateThread, 0, 0, 0);
272  if(!hUpdateThread)
274 
275  return INFO::OK;
276 }
277 
278 static inline void ShutdownUpdateThread()
279 {
280  // signal thread
281  BOOL ok = SetEvent(hExitEvent);
282  WARN_IF_FALSE(ok);
283  // the nice way is to wait for it to exit
284  if(WaitForSingleObject(hUpdateThread, 100) != WAIT_OBJECT_0)
285  TerminateThread(hUpdateThread, 0); // forcibly exit (dangerous)
286  CloseHandle(hExitEvent);
287  CloseHandle(hUpdateThread);
288 }
289 
290 
291 //-----------------------------------------------------------------------------
292 
294 {
295  InitCounter();
296 
297  // latch initial counter value so that timer starts at 0
298  ts->counter = Counter(); // must come before UpdateTimerState
299 
300  UpdateTimerState(); // must come before InitUpdateThread to avoid race
301 
303 
304  return INFO::OK;
305 }
306 
307 
309 {
311 
312  ShutdownCounter();
313 
314  acpi_Shutdown();
315 
316  return INFO::OK;
317 }
#define u8
Definition: types.h:39
#define WINIT_REGISTER_LATE_SHUTDOWN(func)
Definition: winit.h:157
#define UNUSED(param)
mark a function parameter as unused and avoid the corresponding compiler warning. ...
virtual Status Activate()=0
static __declspec(align(64)) TimerState timerStates[2]
static void ShutdownUpdateThread()
Definition: whrt.cpp:278
const Status OK
Definition: status.h:386
some WinAPI functions SetLastError(0) on success, which is bad because it can hide previous errors...
Definition: wutil.h:119
u8 padding[48]
Definition: whrt.cpp:176
static ICounter * counter
Definition: whrt.cpp:96
static void UpdateTimerState()
Definition: whrt.cpp:190
static unsigned __stdcall UpdateThread(void *data)
Definition: whrt.cpp:240
static HANDLE hUpdateThread
Definition: whrt.cpp:238
static const DWORD UPDATE_INTERVAL_MS
Definition: whrt.cpp:235
int BOOL
Definition: wgl.h:51
#define ARRAY_SIZE(name)
LIB_API void debug_SetThreadName(const char *name)
inform the debugger of the current thread&#39;s name.
Definition: bdbg.cpp:126
virtual double NominalFrequency() const =0
initial measurement of the tick rate.
virtual const wchar_t * Name() const =0
#define ENSURE(expr)
ensure the expression &lt;expr&gt; evaluates to non-zero.
Definition: debug.h:282
virtual double Resolution() const =0
actual resolution [s].
const Status LIMIT
Definition: status.h:428
static void InitCounter()
Definition: whrt.cpp:103
double whrt_Resolution()
Definition: whrt.cpp:143
void * HANDLE
Definition: wgl.h:62
static size_t counterBits
Definition: whrt.cpp:100
unsigned long DWORD
Definition: wgl.h:56
static u64 counterMask
Definition: whrt.cpp:101
void acpi_Shutdown()
invalidates all pointers returned by acpi_GetTable.
Definition: acpi.cpp:346
static u64 Counter()
Definition: whrt.cpp:129
const Status WHRT_COUNTER_UNSAFE
Definition: whrt.cpp:46
i64 Status
Error handling system.
Definition: status.h:171
double time
Definition: whrt.cpp:174
static Status whrt_Shutdown()
Definition: whrt.cpp:308
static u64 CounterDelta(u64 oldCounter, u64 newCounter)
Definition: whrt.cpp:138
double whrt_Time()
Definition: whrt.cpp:208
u64 counter
Definition: whrt.cpp:168
#define u64
Definition: types.h:42
#define i64
Definition: types.h:37
static Status ActivateCounter(ICounter *counter)
Definition: whrt.cpp:54
static double nominalFrequency
Definition: whrt.cpp:98
wchar_t * StatusDescription(Status status, wchar_t *buf, size_t max_chars)
generate textual description of a Status.
Definition: status.cpp:79
#define WARN_IF_FALSE(expression)
Definition: status.h:360
static double resolution
Definition: whrt.cpp:99
static HANDLE hExitEvent
Definition: whrt.cpp:237
#define WARN_RETURN(status)
Definition: status.h:255
virtual u64 Counter() const =0
static ICounter * GetNextBestSafeCounter()
Definition: whrt.cpp:68
static Status InitUpdateThread()
Definition: whrt.cpp:257
static void ShutdownCounter()
Definition: whrt.cpp:124
void DestroyCounter(ICounter *&counter)
shut down the counter, free its resources and zero its pointer.
Definition: counter.cpp:109
stores all timer state shared between readers and the update thread.
Definition: whrt.cpp:165
void debug_printf(const wchar_t *fmt,...)
write a formatted string to the debug channel, subject to filtering (see below).
Definition: debug.cpp:142
static enum @41 state
virtual bool IsSafe() const =0
static volatile TimerState *volatile ts
Definition: whrt.cpp:187
static Status whrt_Init()
Definition: whrt.cpp:293
virtual size_t CounterBits() const =0
#define RETURN_STATUS_IF_ERR(expression)
Definition: status.h:276
#define WINIT_REGISTER_EARLY_INIT2(func)
Definition: winit.h:144
ICounter * CreateCounter(size_t id)
Definition: counter.cpp:82
static volatile TimerState *volatile ts2
Definition: whrt.cpp:188