Skip to content

Commit f5b146b

Browse files
committed
[Windows][Concurrency] Use the same clock as Dispatch.
The Concurrency runtime calculates deadlines for scheduling itself using `swift_get_time()`; unfortunately, on Windows that was using `QueryPerformanceCounter()`, while Dispatch uses `QueryInterruptTimePrecise()`. The problem with that is that the two do not necessarily correspond *at all*. In general `QueryPerformanceCounter()` may be using any of a number of hardware timers depending on the machine on which we're running. In the VM I was testing on, the two differed by 20ms, but the worst case is that they are completely unrelated, in which case `Task.sleep()` will wait essentially a random amount of time. rdar://135413803
1 parent 601a5c3 commit f5b146b

File tree

2 files changed

+36
-14
lines changed

2 files changed

+36
-14
lines changed

stdlib/public/Concurrency/CMakeLists.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ set(SWIFT_RUNTIME_CONCURRENCY_SWIFT_FLAGS -I${CMAKE_CURRENT_SOURCE_DIR}/Internal
1515

1616
set(swift_concurrency_private_link_libraries)
1717
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
18-
list(APPEND swift_concurrency_private_link_libraries Synchronization)
18+
list(APPEND swift_concurrency_private_link_libraries
19+
Synchronization
20+
mincore.lib # For QueryInterruptTime()
21+
)
1922
endif()
2023

2124
set(swift_concurrency_incorporate_object_libraries_so swiftThreading)

stdlib/public/Concurrency/Clock.cpp

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -41,19 +41,36 @@ void swift_get_time(
4141
#elif (defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__wasi__))
4242
clock_gettime(CLOCK_MONOTONIC, &continuous);
4343
#elif defined(_WIN32)
44-
LARGE_INTEGER freq;
45-
QueryPerformanceFrequency(&freq);
46-
LARGE_INTEGER count;
47-
QueryPerformanceCounter(&count);
48-
// Divide count (number of ticks) by frequency (number of ticks per
49-
// second) to get the counter in seconds. We also need to multiply the
50-
// count by 1,000,000,000 to get nanosecond resolution. By multiplying
51-
// first, we maintain high precision. The resulting value is the tick
52-
// count in nanoseconds. Use 128-bit math to avoid overflowing.
53-
auto quadPart = static_cast<unsigned _BitInt(128)>(count.QuadPart);
54-
auto ns = (quadPart * 1'000'000'000) / freq.QuadPart;
55-
continuous.tv_sec = ns / 1'000'000'000;
56-
continuous.tv_nsec = ns % 1'000'000'000;
44+
// This needs to match what swift-corelibs-libdispatch does
45+
46+
// QueryInterruptTimePrecise() was added in Windows 10 and is, as
47+
// the name suggests, more precise than QueryInterruptTime().
48+
// Unfortunately, the symbol is not listed in any .lib file in the SDK and
49+
// must be looked up dynamically at runtime even if our minimum deployment
50+
// target is Windows 10.
51+
typedef decltype(QueryInterruptTimePrecise) *QueryITP_FP;
52+
static QueryITP_FP queryITP = nullptr;
53+
static swift::once_t onceToken;
54+
swift::once(onceToken, [] {
55+
if (HMODULE hKernelBase = GetModuleHandleW(L"KernelBase.dll")) {
56+
queryITP = reinterpret_cast<QueryITP_FP>(
57+
GetProcAddress(hKernelBase, "QueryInterruptTimePrecise")
58+
);
59+
}
60+
});
61+
62+
// Call whichever API is available. Both output a value measured in 100ns
63+
// units. We must divide the output by 10,000,000 to get a value in
64+
// seconds and multiply the remainder by 100 to get nanoseconds.
65+
ULONGLONG interruptTime;
66+
if (queryITP) {
67+
(* queryITP)(&interruptTime);
68+
} else {
69+
// Fall back to the older, less precise API.
70+
(void)QueryInterruptTime(&interruptTime);
71+
}
72+
continuous.tv_sec = interruptTime / 10'000'000;
73+
continuous.tv_nsec = (interruptTime % 10'000'000) * 100;
5774
#else
5875
#error Missing platform continuous time definition
5976
#endif
@@ -72,6 +89,8 @@ void swift_get_time(
7289
#elif (defined(__OpenBSD__) || defined(__FreeBSD__))
7390
clock_gettime(CLOCK_UPTIME, &suspending);
7491
#elif defined(_WIN32)
92+
// This needs to match what swift-corelibs-libdispatch does
93+
7594
// QueryUnbiasedInterruptTimePrecise() was added in Windows 10 and is, as
7695
// the name suggests, more precise than QueryUnbiasedInterruptTime().
7796
// Unfortunately, the symbol is not listed in any .lib file in the SDK and

0 commit comments

Comments
 (0)