diff --git a/libc/calls/clktck.c b/libc/calls/clktck.c index 471154777..3c06d2a8e 100644 --- a/libc/calls/clktck.c +++ b/libc/calls/clktck.c @@ -40,7 +40,11 @@ static dontinline int __clk_tck_init(void) { size_t len; struct clockinfo_netbsd clock; if (IsWindows()) { - x = 1000; + // MSVC defines CLK_TCK as 1000 but 1ms is obviously not the + // scheduling quantum Windows actually uses. If we define it + // as 30 rather than 1000, then clock_nanosleep is much more + // accurately able to predict the duration of its busy waits + x = 30; } else if (IsXnu() || IsOpenbsd()) { x = 100; } else if (IsFreebsd()) { diff --git a/libc/calls/clock_getres.c b/libc/calls/clock_getres.c index a54bfdb05..ae343452d 100644 --- a/libc/calls/clock_getres.c +++ b/libc/calls/clock_getres.c @@ -25,14 +25,20 @@ #include "libc/sysv/errfuns.h" #include "libc/time/time.h" -static int sys_clock_getres_poly(int clock, struct timespec *ts, int64_t real) { +static int sys_clock_getres_poly(int clock, struct timespec *ts, int64_t real, + int64_t real_coarse, int64_t boot) { + ts->tv_sec = 0; if (clock == CLOCK_REALTIME) { - ts->tv_sec = 0; ts->tv_nsec = real; return 0; + } else if (clock == CLOCK_REALTIME_COARSE) { + ts->tv_nsec = real_coarse; + return 0; } else if (clock == CLOCK_MONOTONIC) { - ts->tv_sec = 0; - ts->tv_nsec = 1; + ts->tv_nsec = 10; + return 0; + } else if (clock == CLOCK_BOOTTIME) { + ts->tv_nsec = boot; return 0; } else { return einval(); @@ -40,11 +46,11 @@ static int sys_clock_getres_poly(int clock, struct timespec *ts, int64_t real) { } static int sys_clock_getres_nt(int clock, struct timespec *ts) { - return sys_clock_getres_poly(clock, ts, 100); + return sys_clock_getres_poly(clock, ts, 100, 1000000, 1000000); } static int sys_clock_getres_xnu(int clock, struct timespec *ts) { - return sys_clock_getres_poly(clock, ts, 1000); + return sys_clock_getres_poly(clock, ts, 1000, 1000, 1000); } /** diff --git a/libc/calls/clock_gettime-nt.c b/libc/calls/clock_gettime-nt.c index 0518d2275..95abfb526 100644 --- a/libc/calls/clock_gettime-nt.c +++ b/libc/calls/clock_gettime-nt.c @@ -18,26 +18,61 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/struct/timespec.h" #include "libc/calls/struct/timespec.internal.h" +#include "libc/dce.h" #include "libc/errno.h" #include "libc/fmt/wintime.internal.h" -#include "libc/nt/struct/filetime.h" #include "libc/nt/synchronization.h" -#include "libc/sysv/consts/clock.h" + +#define _CLOCK_REALTIME 0 +#define _CLOCK_MONOTONIC 1 +#define _CLOCK_REALTIME_COARSE 2 +#define _CLOCK_BOOTTIME 3 + +static struct { + uint64_t base; + uint64_t freq; +} g_winclock; textwindows int sys_clock_gettime_nt(int clock, struct timespec *ts) { + uint64_t t; struct NtFileTime ft; - if (clock == CLOCK_REALTIME) { - if (!ts) return 0; - GetSystemTimeAsFileTime(&ft); - *ts = FileTimeToTimeSpec(ft); - return 0; - } else if (clock == CLOCK_MONOTONIC) { - if (!ts) return 0; - return sys_clock_gettime_mono(ts); - } else if (clock == CLOCK_BOOTTIME) { - if (ts) *ts = timespec_frommillis(GetTickCount64()); - return 0; - } else { - return -EINVAL; + switch (clock) { + case _CLOCK_REALTIME: + if (ts) { + GetSystemTimePreciseAsFileTime(&ft); + *ts = FileTimeToTimeSpec(ft); + } + return 0; + case _CLOCK_REALTIME_COARSE: + if (ts) { + GetSystemTimeAsFileTime(&ft); + *ts = FileTimeToTimeSpec(ft); + } + return 0; + case _CLOCK_MONOTONIC: + if (ts) { + QueryPerformanceCounter(&t); + t = ((t - g_winclock.base) * 1000000000) / g_winclock.freq; + *ts = timespec_fromnanos(t); + } + return 0; + case _CLOCK_BOOTTIME: + if (ts) { + *ts = timespec_frommillis(GetTickCount64()); + } + return 0; + default: + return -EINVAL; } } + +static textstartup void winclock_init() { + if (IsWindows()) { + QueryPerformanceCounter(&g_winclock.base); + QueryPerformanceFrequency(&g_winclock.freq); + } +} + +const void *const winclock_ctor[] initarray = { + winclock_init, +}; diff --git a/libc/calls/utimensat-nt.c b/libc/calls/utimensat-nt.c index 60accb519..53e103554 100644 --- a/libc/calls/utimensat-nt.c +++ b/libc/calls/utimensat-nt.c @@ -65,7 +65,7 @@ static textwindows int sys_utimensat_nt_impl(int dirfd, const char *path, } if (!ts || ts[0].tv_nsec == UTIME_NOW || ts[1].tv_nsec == UTIME_NOW) { - GetSystemTimeAsFileTime(ft); + GetSystemTimePreciseAsFileTime(ft); } if (ts) { for (i = 0; i < 2; ++i) { diff --git a/libc/nt/synchronization.h b/libc/nt/synchronization.h index a1d2ea253..bbcff07b1 100644 --- a/libc/nt/synchronization.h +++ b/libc/nt/synchronization.h @@ -59,7 +59,7 @@ uint32_t SleepEx(uint32_t dwMilliseconds, bool32 bAlertable); void GetSystemTime(struct NtSystemTime *lpSystemTime); bool32 SystemTimeToFileTime(const struct NtSystemTime *lpSystemTime, struct NtFileTime *lpFileTime); -void GetSystemTimeAsFileTime(struct NtFileTime *); /* win8+ */ +void GetSystemTimeAsFileTime(struct NtFileTime *); void GetSystemTimePreciseAsFileTime(struct NtFileTime *); /* win8+ */ uint32_t WaitForSingleObject(int64_t hHandle, uint32_t dwMilliseconds); @@ -110,8 +110,8 @@ void TryAcquireSRWLockShared(intptr_t *); uint64_t GetTickCount64(void); -bool32 QueryPerformanceFrequency(int64_t *lpFrequency); -bool32 QueryPerformanceCounter(int64_t *lpPerformanceCount); +bool32 QueryPerformanceFrequency(uint64_t *lpFrequency); +bool32 QueryPerformanceCounter(uint64_t *lpPerformanceCount); bool32 GetSystemTimeAdjustment(uint32_t *lpTimeAdjustment, uint32_t *lpTimeIncrement, bool32 *lpTimeAdjustmentDisabled); diff --git a/libc/sysv/consts.sh b/libc/sysv/consts.sh index 498ec69a7..142f73d4b 100755 --- a/libc/sysv/consts.sh +++ b/libc/sysv/consts.sh @@ -578,7 +578,7 @@ syscon close CLOSE_RANGE_CLOEXEC 4 4 -1 -1 -1 -1 -1 -1 # syscon clock CLOCK_REALTIME 0 0 0 0 0 0 0 0 # consensus syscon clock CLOCK_REALTIME_PRECISE 0 0 0 0 9 0 0 0 # syscon clock CLOCK_REALTIME_FAST 0 0 0 0 10 0 0 0 # -syscon clock CLOCK_REALTIME_COARSE 5 5 0 0 10 0 0 0 # Linux 2.6.32+; bsd consensus; not available on RHEL5 +syscon clock CLOCK_REALTIME_COARSE 5 5 0 0 10 0 0 2 # Linux 2.6.32+; bsd consensus; not available on RHEL5 syscon clock CLOCK_MONOTONIC 1 1 1 6 4 3 3 1 # XNU/NT faked; could move backwards if NTP introduces negative leap second syscon clock CLOCK_MONOTONIC_PRECISE 1 1 1 6 11 3 3 1 # 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 syscon clock CLOCK_PROCESS_CPUTIME_ID 2 2 127 12 15 2 0x40000000 127 # NetBSD lets you bitwise a PID into clockid_t syscon clock CLOCK_THREAD_CPUTIME_ID 3 3 127 16 14 4 0x20000000 127 # syscon clock CLOCK_PROF 127 127 127 127 2 127 2 127 # -syscon clock CLOCK_BOOTTIME 7 7 7 127 127 6 127 7 # +syscon clock CLOCK_BOOTTIME 7 7 7 127 127 6 127 3 # syscon clock CLOCK_REALTIME_ALARM 8 8 127 127 127 127 127 127 # syscon clock CLOCK_BOOTTIME_ALARM 9 9 127 127 127 127 127 127 # syscon clock CLOCK_TAI 11 11 127 127 127 127 127 127 # diff --git a/libc/sysv/consts/CLOCK_BOOTTIME.S b/libc/sysv/consts/CLOCK_BOOTTIME.S index 872e45fb2..ead5f9008 100644 --- a/libc/sysv/consts/CLOCK_BOOTTIME.S +++ b/libc/sysv/consts/CLOCK_BOOTTIME.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon clock,CLOCK_BOOTTIME,7,7,7,127,127,6,127,7 +.syscon clock,CLOCK_BOOTTIME,7,7,7,127,127,6,127,3 diff --git a/libc/sysv/consts/CLOCK_REALTIME_COARSE.S b/libc/sysv/consts/CLOCK_REALTIME_COARSE.S index 42d0bf340..1dfdb7474 100644 --- a/libc/sysv/consts/CLOCK_REALTIME_COARSE.S +++ b/libc/sysv/consts/CLOCK_REALTIME_COARSE.S @@ -1,2 +1,2 @@ #include "libc/sysv/consts/syscon.internal.h" -.syscon clock,CLOCK_REALTIME_COARSE,5,5,0,0,10,0,0,0 +.syscon clock,CLOCK_REALTIME_COARSE,5,5,0,0,10,0,0,2 diff --git a/test/libc/calls/clock_gettime_test.c b/test/libc/calls/clock_gettime_test.c index bff8c1792..6063a82d0 100644 --- a/test/libc/calls/clock_gettime_test.c +++ b/test/libc/calls/clock_gettime_test.c @@ -50,7 +50,7 @@ TEST(clock_gettime, testClockRealtime) { EXPECT_LT((unsigned)ABS(ts.tv_sec - tv.tv_sec), 5u); } -BENCH(clock_gettime, bench) { +TEST(clock_gettime, bench) { struct timeval tv; struct timespec ts; gettimeofday(&tv, 0); // trigger init