Skip to content

Commit 8caf1b4

Browse files
committed
Improve time/sleep accuracy on Windows
It's now almost as good as Linux thanks to a Windows 8+ API.
1 parent 72ac5f1 commit 8caf1b4

File tree

9 files changed

+76
-31
lines changed

9 files changed

+76
-31
lines changed

libc/calls/clktck.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,11 @@ static dontinline int __clk_tck_init(void) {
4040
size_t len;
4141
struct clockinfo_netbsd clock;
4242
if (IsWindows()) {
43-
x = 1000;
43+
// MSVC defines CLK_TCK as 1000 but 1ms is obviously not the
44+
// scheduling quantum Windows actually uses. If we define it
45+
// as 30 rather than 1000, then clock_nanosleep is much more
46+
// accurately able to predict the duration of its busy waits
47+
x = 30;
4448
} else if (IsXnu() || IsOpenbsd()) {
4549
x = 100;
4650
} else if (IsFreebsd()) {

libc/calls/clock_getres.c

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,26 +25,32 @@
2525
#include "libc/sysv/errfuns.h"
2626
#include "libc/time/time.h"
2727

28-
static int sys_clock_getres_poly(int clock, struct timespec *ts, int64_t real) {
28+
static int sys_clock_getres_poly(int clock, struct timespec *ts, int64_t real,
29+
int64_t real_coarse, int64_t boot) {
30+
ts->tv_sec = 0;
2931
if (clock == CLOCK_REALTIME) {
30-
ts->tv_sec = 0;
3132
ts->tv_nsec = real;
3233
return 0;
34+
} else if (clock == CLOCK_REALTIME_COARSE) {
35+
ts->tv_nsec = real_coarse;
36+
return 0;
3337
} else if (clock == CLOCK_MONOTONIC) {
34-
ts->tv_sec = 0;
35-
ts->tv_nsec = 1;
38+
ts->tv_nsec = 10;
39+
return 0;
40+
} else if (clock == CLOCK_BOOTTIME) {
41+
ts->tv_nsec = boot;
3642
return 0;
3743
} else {
3844
return einval();
3945
}
4046
}
4147

4248
static int sys_clock_getres_nt(int clock, struct timespec *ts) {
43-
return sys_clock_getres_poly(clock, ts, 100);
49+
return sys_clock_getres_poly(clock, ts, 100, 1000000, 1000000);
4450
}
4551

4652
static int sys_clock_getres_xnu(int clock, struct timespec *ts) {
47-
return sys_clock_getres_poly(clock, ts, 1000);
53+
return sys_clock_getres_poly(clock, ts, 1000, 1000, 1000);
4854
}
4955

5056
/**

libc/calls/clock_gettime-nt.c

Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,26 +18,61 @@
1818
╚─────────────────────────────────────────────────────────────────────────────*/
1919
#include "libc/calls/struct/timespec.h"
2020
#include "libc/calls/struct/timespec.internal.h"
21+
#include "libc/dce.h"
2122
#include "libc/errno.h"
2223
#include "libc/fmt/wintime.internal.h"
23-
#include "libc/nt/struct/filetime.h"
2424
#include "libc/nt/synchronization.h"
25-
#include "libc/sysv/consts/clock.h"
25+
26+
#define _CLOCK_REALTIME 0
27+
#define _CLOCK_MONOTONIC 1
28+
#define _CLOCK_REALTIME_COARSE 2
29+
#define _CLOCK_BOOTTIME 3
30+
31+
static struct {
32+
uint64_t base;
33+
uint64_t freq;
34+
} g_winclock;
2635

2736
textwindows int sys_clock_gettime_nt(int clock, struct timespec *ts) {
37+
uint64_t t;
2838
struct NtFileTime ft;
29-
if (clock == CLOCK_REALTIME) {
30-
if (!ts) return 0;
31-
GetSystemTimeAsFileTime(&ft);
32-
*ts = FileTimeToTimeSpec(ft);
33-
return 0;
34-
} else if (clock == CLOCK_MONOTONIC) {
35-
if (!ts) return 0;
36-
return sys_clock_gettime_mono(ts);
37-
} else if (clock == CLOCK_BOOTTIME) {
38-
if (ts) *ts = timespec_frommillis(GetTickCount64());
39-
return 0;
40-
} else {
41-
return -EINVAL;
39+
switch (clock) {
40+
case _CLOCK_REALTIME:
41+
if (ts) {
42+
GetSystemTimePreciseAsFileTime(&ft);
43+
*ts = FileTimeToTimeSpec(ft);
44+
}
45+
return 0;
46+
case _CLOCK_REALTIME_COARSE:
47+
if (ts) {
48+
GetSystemTimeAsFileTime(&ft);
49+
*ts = FileTimeToTimeSpec(ft);
50+
}
51+
return 0;
52+
case _CLOCK_MONOTONIC:
53+
if (ts) {
54+
QueryPerformanceCounter(&t);
55+
t = ((t - g_winclock.base) * 1000000000) / g_winclock.freq;
56+
*ts = timespec_fromnanos(t);
57+
}
58+
return 0;
59+
case _CLOCK_BOOTTIME:
60+
if (ts) {
61+
*ts = timespec_frommillis(GetTickCount64());
62+
}
63+
return 0;
64+
default:
65+
return -EINVAL;
66+
}
67+
}
68+
69+
static textstartup void winclock_init() {
70+
if (IsWindows()) {
71+
QueryPerformanceCounter(&g_winclock.base);
72+
QueryPerformanceFrequency(&g_winclock.freq);
4273
}
4374
}
75+
76+
const void *const winclock_ctor[] initarray = {
77+
winclock_init,
78+
};

libc/calls/utimensat-nt.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ static textwindows int sys_utimensat_nt_impl(int dirfd, const char *path,
6565
}
6666

6767
if (!ts || ts[0].tv_nsec == UTIME_NOW || ts[1].tv_nsec == UTIME_NOW) {
68-
GetSystemTimeAsFileTime(ft);
68+
GetSystemTimePreciseAsFileTime(ft);
6969
}
7070
if (ts) {
7171
for (i = 0; i < 2; ++i) {

libc/nt/synchronization.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ uint32_t SleepEx(uint32_t dwMilliseconds, bool32 bAlertable);
5959
void GetSystemTime(struct NtSystemTime *lpSystemTime);
6060
bool32 SystemTimeToFileTime(const struct NtSystemTime *lpSystemTime,
6161
struct NtFileTime *lpFileTime);
62-
void GetSystemTimeAsFileTime(struct NtFileTime *); /* win8+ */
62+
void GetSystemTimeAsFileTime(struct NtFileTime *);
6363
void GetSystemTimePreciseAsFileTime(struct NtFileTime *); /* win8+ */
6464

6565
uint32_t WaitForSingleObject(int64_t hHandle, uint32_t dwMilliseconds);
@@ -110,8 +110,8 @@ void TryAcquireSRWLockShared(intptr_t *);
110110

111111
uint64_t GetTickCount64(void);
112112

113-
bool32 QueryPerformanceFrequency(int64_t *lpFrequency);
114-
bool32 QueryPerformanceCounter(int64_t *lpPerformanceCount);
113+
bool32 QueryPerformanceFrequency(uint64_t *lpFrequency);
114+
bool32 QueryPerformanceCounter(uint64_t *lpPerformanceCount);
115115
bool32 GetSystemTimeAdjustment(uint32_t *lpTimeAdjustment,
116116
uint32_t *lpTimeIncrement,
117117
bool32 *lpTimeAdjustmentDisabled);

libc/sysv/consts.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -578,7 +578,7 @@ syscon close CLOSE_RANGE_CLOEXEC 4 4 -1 -1 -1 -1 -1 -1 #
578578
syscon clock CLOCK_REALTIME 0 0 0 0 0 0 0 0 # consensus
579579
syscon clock CLOCK_REALTIME_PRECISE 0 0 0 0 9 0 0 0 #
580580
syscon clock CLOCK_REALTIME_FAST 0 0 0 0 10 0 0 0 #
581-
syscon clock CLOCK_REALTIME_COARSE 5 5 0 0 10 0 0 0 # Linux 2.6.32+; bsd consensus; not available on RHEL5
581+
syscon clock CLOCK_REALTIME_COARSE 5 5 0 0 10 0 0 2 # Linux 2.6.32+; bsd consensus; not available on RHEL5
582582
syscon clock CLOCK_MONOTONIC 1 1 1 6 4 3 3 1 # XNU/NT faked; could move backwards if NTP introduces negative leap second
583583
syscon clock CLOCK_MONOTONIC_PRECISE 1 1 1 6 11 3 3 1 #
584584
syscon clock CLOCK_MONOTONIC_FAST 1 1 1 6 12 3 3 1 #
@@ -587,7 +587,7 @@ syscon clock CLOCK_MONOTONIC_RAW 4 4 127 4 127 127 127 127 # a
587587
syscon clock CLOCK_PROCESS_CPUTIME_ID 2 2 127 12 15 2 0x40000000 127 # NetBSD lets you bitwise a PID into clockid_t
588588
syscon clock CLOCK_THREAD_CPUTIME_ID 3 3 127 16 14 4 0x20000000 127 #
589589
syscon clock CLOCK_PROF 127 127 127 127 2 127 2 127 #
590-
syscon clock CLOCK_BOOTTIME 7 7 7 127 127 6 127 7 #
590+
syscon clock CLOCK_BOOTTIME 7 7 7 127 127 6 127 3 #
591591
syscon clock CLOCK_REALTIME_ALARM 8 8 127 127 127 127 127 127 #
592592
syscon clock CLOCK_BOOTTIME_ALARM 9 9 127 127 127 127 127 127 #
593593
syscon clock CLOCK_TAI 11 11 127 127 127 127 127 127 #

libc/sysv/consts/CLOCK_BOOTTIME.S

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
#include "libc/sysv/consts/syscon.internal.h"
2-
.syscon clock,CLOCK_BOOTTIME,7,7,7,127,127,6,127,7
2+
.syscon clock,CLOCK_BOOTTIME,7,7,7,127,127,6,127,3
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
#include "libc/sysv/consts/syscon.internal.h"
2-
.syscon clock,CLOCK_REALTIME_COARSE,5,5,0,0,10,0,0,0
2+
.syscon clock,CLOCK_REALTIME_COARSE,5,5,0,0,10,0,0,2

test/libc/calls/clock_gettime_test.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ TEST(clock_gettime, testClockRealtime) {
5050
EXPECT_LT((unsigned)ABS(ts.tv_sec - tv.tv_sec), 5u);
5151
}
5252

53-
BENCH(clock_gettime, bench) {
53+
TEST(clock_gettime, bench) {
5454
struct timeval tv;
5555
struct timespec ts;
5656
gettimeofday(&tv, 0); // trigger init

0 commit comments

Comments
 (0)