diff --git a/stdlib/public/Concurrency/Clock.cpp b/stdlib/public/Concurrency/Clock.cpp index 4ad1c1bb50662..b661eb14d8d20 100644 --- a/stdlib/public/Concurrency/Clock.cpp +++ b/stdlib/public/Concurrency/Clock.cpp @@ -11,17 +11,18 @@ //===----------------------------------------------------------------------===// #include "swift/Runtime/Concurrency.h" +#include "swift/Runtime/Once.h" -#if __has_include() -#define HAS_TIME 1 #include -#endif #if defined(_WIN32) #define WIN32_LEAN_AND_MEAN #define NOMINMAX #include +#include #endif +#include "Error.h" + using namespace swift; SWIFT_EXPORT_FROM(swift_Concurrency) @@ -32,80 +33,83 @@ void swift_get_time( swift_clock_id clock_id) { switch (clock_id) { case swift_clock_id_continuous: { -#if defined(__linux__) && HAS_TIME struct timespec continuous; +#if defined(__linux__) clock_gettime(CLOCK_BOOTTIME, &continuous); - *seconds = continuous.tv_sec; - *nanoseconds = continuous.tv_nsec; -#elif defined(__APPLE__) && HAS_TIME - struct timespec continuous; +#elif defined(__APPLE__) clock_gettime(CLOCK_MONOTONIC_RAW, &continuous); - *seconds = continuous.tv_sec; - *nanoseconds = continuous.tv_nsec; -#elif (defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__wasi__)) && HAS_TIME - struct timespec continuous; +#elif (defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__wasi__)) clock_gettime(CLOCK_MONOTONIC, &continuous); - *seconds = continuous.tv_sec; - *nanoseconds = continuous.tv_nsec; #elif defined(_WIN32) LARGE_INTEGER freq; QueryPerformanceFrequency(&freq); LARGE_INTEGER count; QueryPerformanceCounter(&count); - *seconds = count.QuadPart / freq.QuadPart; - if (freq.QuadPart < 1000000000) { - *nanoseconds = - ((count.QuadPart % freq.QuadPart) * 1000000000) / freq.QuadPart; - } else { - *nanoseconds = - (count.QuadPart % freq.QuadPart) * (1000000000.0 / freq.QuadPart); - } + // Divide count (number of ticks) by frequency (number of ticks per + // second) to get the counter in seconds. We also need to multiply the + // count by 1,000,000,000 to get nanosecond resolution. By multiplying + // first, we maintain high precision. The resulting value is the tick + // count in nanoseconds. Use 128-bit math to avoid overflowing. + auto quadPart = static_cast(count.QuadPart); + auto ns = (quadPart * 1'000'000'000) / freq.QuadPart; + continuous.tv_sec = ns / 1'000'000'000; + continuous.tv_nsec = ns % 1'000'000'000; #else #error Missing platform continuous time definition #endif + *seconds = continuous.tv_sec; + *nanoseconds = continuous.tv_nsec; return; } case swift_clock_id_suspending: { -#if defined(__linux__) && HAS_TIME struct timespec suspending; +#if defined(__linux__) clock_gettime(CLOCK_MONOTONIC, &suspending); - *seconds = suspending.tv_sec; - *nanoseconds = suspending.tv_nsec; -#elif defined(__APPLE__) && HAS_TIME - struct timespec suspending; +#elif defined(__APPLE__) clock_gettime(CLOCK_UPTIME_RAW, &suspending); - *seconds = suspending.tv_sec; - *nanoseconds = suspending.tv_nsec; -#elif defined(__wasi__) && HAS_TIME - struct timespec suspending; +#elif defined(__wasi__) clock_gettime(CLOCK_MONOTONIC, &suspending); - *seconds = suspending.tv_sec; - *nanoseconds = suspending.tv_nsec; -#elif (defined(__OpenBSD__) || defined(__FreeBSD__)) && HAS_TIME - struct timespec suspending; +#elif (defined(__OpenBSD__) || defined(__FreeBSD__)) clock_gettime(CLOCK_UPTIME, &suspending); - *seconds = suspending.tv_sec; - *nanoseconds = suspending.tv_nsec; #elif defined(_WIN32) - LARGE_INTEGER freq; - QueryPerformanceFrequency(&freq); - LARGE_INTEGER count; - QueryPerformanceCounter(&count); - *seconds = count.QuadPart / freq.QuadPart; - if (freq.QuadPart < 1000000000) { - *nanoseconds = - ((count.QuadPart % freq.QuadPart) * 1000000000) / freq.QuadPart; + // QueryUnbiasedInterruptTimePrecise() was added in Windows 10 and is, as + // the name suggests, more precise than QueryUnbiasedInterruptTime(). + // Unfortunately, the symbol is not listed in any .lib file in the SDK and + // must be looked up dynamically at runtime even if our minimum deployment + // target is Windows 10. + typedef decltype(QueryUnbiasedInterruptTimePrecise) *QueryUITP_FP; + static QueryUITP_FP queryUITP = nullptr; + static swift::once_t onceToken; + swift::once(onceToken, [] { + if (HMODULE hKernelBase = GetModuleHandleW(L"KernelBase.dll")) { + queryUITP = reinterpret_cast( + GetProcAddress(hKernelBase, "QueryUnbiasedInterruptTimePrecise") + ); + } + }); + + // Call whichever API is available. Both output a value measured in 100ns + // units. We must divide the output by 10,000,000 to get a value in + // seconds and multiply the remainder by 100 to get nanoseconds. + ULONGLONG unbiasedTime; + if (queryUITP) { + (* queryUITP)(&unbiasedTime); } else { - *nanoseconds = - (count.QuadPart % freq.QuadPart) * (1000000000.0 / freq.QuadPart); + // Fall back to the older, less precise API. + (void)QueryUnbiasedInterruptTime(&unbiasedTime); } + suspending.tv_sec = unbiasedTime / 10'000'000; + suspending.tv_nsec = (unbiasedTime % 10'000'000) * 100; #else #error Missing platform suspending time definition #endif + *seconds = suspending.tv_sec; + *nanoseconds = suspending.tv_nsec; return; } } - abort(); // Invalid clock_id + swift_Concurrency_fatalError(0, "Fatal error: invalid clock ID %d\n", + clock_id); } SWIFT_EXPORT_FROM(swift_Concurrency) @@ -116,55 +120,46 @@ void swift_get_clock_res( swift_clock_id clock_id) { switch (clock_id) { case swift_clock_id_continuous: { -#if defined(__linux__) && HAS_TIME struct timespec continuous; +#if defined(__linux__) clock_getres(CLOCK_BOOTTIME, &continuous); - *seconds = continuous.tv_sec; - *nanoseconds = continuous.tv_nsec; -#elif defined(__APPLE__) && HAS_TIME - struct timespec continuous; +#elif defined(__APPLE__) clock_getres(CLOCK_MONOTONIC_RAW, &continuous); - *seconds = continuous.tv_sec; - *nanoseconds = continuous.tv_nsec; -#elif (defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__wasi__)) && HAS_TIME - struct timespec continuous; +#elif (defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__wasi__)) clock_getres(CLOCK_MONOTONIC, &continuous); - *seconds = continuous.tv_sec; - *nanoseconds = continuous.tv_nsec; #elif defined(_WIN32) - *seconds = 0; - *nanoseconds = 1000; + LARGE_INTEGER freq; + QueryPerformanceFrequency(&freq); + continuous.tv_sec = 0; + continuous.tv_nsec = 1'000'000'000 / freq.QuadPart; #else #error Missing platform continuous time definition #endif + *seconds = continuous.tv_sec; + *nanoseconds = continuous.tv_nsec; return; } case swift_clock_id_suspending: { struct timespec suspending; -#if defined(__linux__) && HAS_TIME +#if defined(__linux__) clock_getres(CLOCK_MONOTONIC_RAW, &suspending); - *seconds = suspending.tv_sec; - *nanoseconds = suspending.tv_nsec; -#elif defined(__APPLE__) && HAS_TIME +#elif defined(__APPLE__) clock_getres(CLOCK_UPTIME_RAW, &suspending); - *seconds = suspending.tv_sec; - *nanoseconds = suspending.tv_nsec; -#elif defined(__wasi__) && HAS_TIME +#elif defined(__wasi__) clock_getres(CLOCK_MONOTONIC, &suspending); - *seconds = suspending.tv_sec; - *nanoseconds = suspending.tv_nsec; -#elif (defined(__OpenBSD__) || defined(__FreeBSD__)) && HAS_TIME +#elif (defined(__OpenBSD__) || defined(__FreeBSD__)) clock_getres(CLOCK_UPTIME, &suspending); - *seconds = suspending.tv_sec; - *nanoseconds = suspending.tv_nsec; #elif defined(_WIN32) - *seconds = 0; - *nanoseconds = 1000; + suspending.tv_sec = 0; + suspending.tv_nsec = 100; #else #error Missing platform suspending time definition #endif + *seconds = suspending.tv_sec; + *nanoseconds = suspending.tv_nsec; return; } } - abort(); // Invalid clock_id + swift_Concurrency_fatalError(0, "Fatal error: invalid clock ID %d\n", + clock_id); }