Skip to content
This repository was archived by the owner on May 4, 2018. It is now read-only.

Commit 6ced8c2

Browse files
orangemochasaghul
authored andcommitted
windows: improve timer precision
Improve timing precision by using QueryPerformanceCounter. This is part of the fix for Node.js' test-timers-first-fire.js.
1 parent 234b1e0 commit 6ced8c2

File tree

5 files changed

+46
-67
lines changed

5 files changed

+46
-67
lines changed

include/uv-win.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -316,8 +316,6 @@ RB_HEAD(uv_timer_tree_s, uv_timer_s);
316316
HANDLE iocp; \
317317
/* The current time according to the event loop. in msecs. */ \
318318
uint64_t time; \
319-
/* GetTickCount() result when the event loop time was last updated. */ \
320-
DWORD last_tick_count; \
321319
/* Tail of a single-linked circular queue of pending reqs. If the queue */ \
322320
/* is empty, tail_ is NULL. If there is only one item, */ \
323321
/* tail_->next_req == tail_ */ \

src/win/core.c

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,6 @@ int uv_loop_init(uv_loop_t* loop) {
137137
* to zero before calling uv_update_time for the first time.
138138
*/
139139
loop->time = 0;
140-
loop->last_tick_count = 0;
141140
uv_update_time(loop);
142141

143142
QUEUE_INIT(&loop->wq);
@@ -314,13 +313,17 @@ static void uv_poll(uv_loop_t* loop, DWORD timeout) {
314313
/* Package was dequeued */
315314
req = uv_overlapped_to_req(overlapped);
316315
uv_insert_pending_req(loop, req);
316+
317+
/* Some time might have passed waiting for I/O,
318+
* so update the loop time here.
319+
*/
320+
uv_update_time(loop);
317321
} else if (GetLastError() != WAIT_TIMEOUT) {
318322
/* Serious error */
319323
uv_fatal_error(GetLastError(), "GetQueuedCompletionStatus");
320-
} else {
321-
/* We're sure that at least `timeout` milliseconds have expired, but
322-
* this may not be reflected yet in the GetTickCount() return value.
323-
* Therefore we ensure it's taken into account here.
324+
} else if (timeout > 0) {
325+
/* GetQueuedCompletionStatus can occasionally return a little early.
326+
* Make sure that the desired timeout is reflected in the loop time.
324327
*/
325328
uv__time_forward(loop, timeout);
326329
}
@@ -347,13 +350,17 @@ static void uv_poll_ex(uv_loop_t* loop, DWORD timeout) {
347350
req = uv_overlapped_to_req(overlappeds[i].lpOverlapped);
348351
uv_insert_pending_req(loop, req);
349352
}
353+
354+
/* Some time might have passed waiting for I/O,
355+
* so update the loop time here.
356+
*/
357+
uv_update_time(loop);
350358
} else if (GetLastError() != WAIT_TIMEOUT) {
351359
/* Serious error */
352360
uv_fatal_error(GetLastError(), "GetQueuedCompletionStatusEx");
353361
} else if (timeout > 0) {
354-
/* We're sure that at least `timeout` milliseconds have expired, but
355-
* this may not be reflected yet in the GetTickCount() return value.
356-
* Therefore we ensure it's taken into account here.
362+
/* GetQueuedCompletionStatus can occasionally return a little early.
363+
* Make sure that the desired timeout is reflected in the loop time.
357364
*/
358365
uv__time_forward(loop, timeout);
359366
}
@@ -412,7 +419,6 @@ int uv_run(uv_loop_t *loop, uv_run_mode mode) {
412419
* UV_RUN_NOWAIT makes no guarantees about progress so it's omitted from
413420
* the check.
414421
*/
415-
uv_update_time(loop);
416422
uv_process_timers(loop);
417423
}
418424

src/win/internal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,7 @@ void uv__fs_poll_endgame(uv_loop_t* loop, uv_fs_poll_t* handle);
322322
*/
323323
void uv__util_init();
324324

325+
uint64_t uv__hrtime(double scale);
325326
int uv_parent_pid();
326327
__declspec(noreturn) void uv_fatal_error(const int errorno, const char* syscall);
327328

src/win/timer.c

Lines changed: 10 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -28,39 +28,17 @@
2828
#include "handle-inl.h"
2929

3030

31-
void uv_update_time(uv_loop_t* loop) {
32-
DWORD ticks;
33-
ULARGE_INTEGER time;
34-
35-
ticks = GetTickCount();
31+
/* The number of milliseconds in one second. */
32+
#define UV__MILLISEC 1000
3633

37-
time.QuadPart = loop->time;
3834

39-
/* GetTickCount() can conceivably wrap around, so when the current tick
40-
* count is lower than the last tick count, we'll assume it has wrapped.
41-
* uv_poll must make sure that the timer can never overflow more than
42-
* once between two subsequent uv_update_time calls.
43-
*/
44-
time.LowPart = ticks;
45-
if (ticks < loop->last_tick_count)
46-
time.HighPart++;
47-
48-
/* Remember the last tick count. */
49-
loop->last_tick_count = ticks;
50-
51-
/* The GetTickCount() resolution isn't too good. Sometimes it'll happen
52-
* that GetQueuedCompletionStatus() or GetQueuedCompletionStatusEx() has
53-
* waited for a couple of ms but this is not reflected in the GetTickCount
54-
* result yet. Therefore whenever GetQueuedCompletionStatus times out
55-
* we'll add the number of ms that it has waited to the current loop time.
56-
* When that happened the loop time might be a little ms farther than what
57-
* we've just computed, and we shouldn't update the loop time.
58-
*/
59-
if (loop->time < time.QuadPart)
60-
loop->time = time.QuadPart;
35+
void uv_update_time(uv_loop_t* loop) {
36+
uint64_t new_time = uv__hrtime(UV__MILLISEC);
37+
if (new_time > loop->time) {
38+
loop->time = new_time;
39+
}
6140
}
6241

63-
6442
void uv__time_forward(uv_loop_t* loop, uint64_t msecs) {
6543
loop->time += msecs;
6644
}
@@ -191,16 +169,9 @@ DWORD uv__next_timeout(const uv_loop_t* loop) {
191169
timer = RB_MIN(uv_timer_tree_s, &((uv_loop_t*)loop)->timers);
192170
if (timer) {
193171
delta = timer->due - loop->time;
194-
if (delta >= UINT_MAX >> 1) {
195-
/* A timeout value of UINT_MAX means infinite, so that's no good. But
196-
* more importantly, there's always the risk that GetTickCount wraps.
197-
* uv_update_time can detect this, but we must make sure that the
198-
* tick counter never overflows twice between two subsequent
199-
* uv_update_time calls. We do this by never sleeping more than half
200-
* the time it takes to wrap the counter - which is huge overkill,
201-
* but hey, it's not so bad to wake up every 25 days.
202-
*/
203-
return UINT_MAX >> 1;
172+
if (delta >= UINT_MAX - 1) {
173+
/* A timeout value of UINT_MAX means infinite, so that's no good. */
174+
return UINT_MAX - 1;
204175
} else if (delta < 0) {
205176
/* Negative timeout values are not allowed */
206177
return 0;

src/win/util.c

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -52,16 +52,15 @@
5252
#define MAX_TITLE_LENGTH 8192
5353

5454
/* The number of nanoseconds in one second. */
55-
#undef NANOSEC
56-
#define NANOSEC 1000000000
55+
#define UV__NANOSEC 1000000000
5756

5857

5958
/* Cached copy of the process title, plus a mutex guarding it. */
6059
static char *process_title;
6160
static CRITICAL_SECTION process_title_lock;
6261

63-
/* Frequency (ticks per nanosecond) of the high-resolution clock. */
64-
static double hrtime_frequency_ = 0;
62+
/* Interval (in seconds) of the high-resolution clock. */
63+
static double hrtime_interval_ = 0;
6564

6665

6766
/*
@@ -73,11 +72,14 @@ void uv__util_init() {
7372
/* Initialize process title access mutex. */
7473
InitializeCriticalSection(&process_title_lock);
7574

76-
/* Retrieve high-resolution timer frequency. */
77-
if (QueryPerformanceFrequency(&perf_frequency))
78-
hrtime_frequency_ = (double) perf_frequency.QuadPart / (double) NANOSEC;
79-
else
80-
hrtime_frequency_= 0;
75+
/* Retrieve high-resolution timer frequency
76+
* and precompute its reciprocal.
77+
*/
78+
if (QueryPerformanceFrequency(&perf_frequency)) {
79+
hrtime_interval_ = 1.0 / perf_frequency.QuadPart;
80+
} else {
81+
hrtime_interval_= 0;
82+
}
8183
}
8284

8385

@@ -463,26 +465,27 @@ int uv_get_process_title(char* buffer, size_t size) {
463465

464466

465467
uint64_t uv_hrtime(void) {
466-
LARGE_INTEGER counter;
467-
468468
uv__once_init();
469+
return uv__hrtime(UV__NANOSEC);
470+
}
471+
472+
uint64_t uv__hrtime(double scale) {
473+
LARGE_INTEGER counter;
469474

470-
/* If the performance frequency is zero, there's no support. */
471-
if (hrtime_frequency_ == 0) {
472-
/* uv__set_sys_error(loop, ERROR_NOT_SUPPORTED); */
475+
/* If the performance interval is zero, there's no support. */
476+
if (hrtime_interval_ == 0) {
473477
return 0;
474478
}
475479

476480
if (!QueryPerformanceCounter(&counter)) {
477-
/* uv__set_sys_error(loop, GetLastError()); */
478481
return 0;
479482
}
480483

481484
/* Because we have no guarantee about the order of magnitude of the
482-
* performance counter frequency, integer math could cause this computation
485+
* performance counter interval, integer math could cause this computation
483486
* to overflow. Therefore we resort to floating point math.
484487
*/
485-
return (uint64_t) ((double) counter.QuadPart / hrtime_frequency_);
488+
return (uint64_t) ((double) counter.QuadPart * hrtime_interval_ * scale);
486489
}
487490

488491

0 commit comments

Comments
 (0)