Skip to content

Commit c8ef200

Browse files
authored
SuspendingClock on Windows does not suspend (#63225)
1 parent 1d6a547 commit c8ef200

File tree

1 file changed

+71
-76
lines changed

1 file changed

+71
-76
lines changed

stdlib/public/Concurrency/Clock.cpp

Lines changed: 71 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,18 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
#include "swift/Runtime/Concurrency.h"
14+
#include "swift/Runtime/Once.h"
1415

15-
#if __has_include(<time.h>)
16-
#define HAS_TIME 1
1716
#include <time.h>
18-
#endif
1917
#if defined(_WIN32)
2018
#define WIN32_LEAN_AND_MEAN
2119
#define NOMINMAX
2220
#include <Windows.h>
21+
#include <realtimeapiset.h>
2322
#endif
2423

24+
#include "Error.h"
25+
2526
using namespace swift;
2627

2728
SWIFT_EXPORT_FROM(swift_Concurrency)
@@ -32,80 +33,83 @@ void swift_get_time(
3233
swift_clock_id clock_id) {
3334
switch (clock_id) {
3435
case swift_clock_id_continuous: {
35-
#if defined(__linux__) && HAS_TIME
3636
struct timespec continuous;
37+
#if defined(__linux__)
3738
clock_gettime(CLOCK_BOOTTIME, &continuous);
38-
*seconds = continuous.tv_sec;
39-
*nanoseconds = continuous.tv_nsec;
40-
#elif defined(__APPLE__) && HAS_TIME
41-
struct timespec continuous;
39+
#elif defined(__APPLE__)
4240
clock_gettime(CLOCK_MONOTONIC_RAW, &continuous);
43-
*seconds = continuous.tv_sec;
44-
*nanoseconds = continuous.tv_nsec;
45-
#elif (defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__wasi__)) && HAS_TIME
46-
struct timespec continuous;
41+
#elif (defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__wasi__))
4742
clock_gettime(CLOCK_MONOTONIC, &continuous);
48-
*seconds = continuous.tv_sec;
49-
*nanoseconds = continuous.tv_nsec;
5043
#elif defined(_WIN32)
5144
LARGE_INTEGER freq;
5245
QueryPerformanceFrequency(&freq);
5346
LARGE_INTEGER count;
5447
QueryPerformanceCounter(&count);
55-
*seconds = count.QuadPart / freq.QuadPart;
56-
if (freq.QuadPart < 1000000000) {
57-
*nanoseconds =
58-
((count.QuadPart % freq.QuadPart) * 1000000000) / freq.QuadPart;
59-
} else {
60-
*nanoseconds =
61-
(count.QuadPart % freq.QuadPart) * (1000000000.0 / freq.QuadPart);
62-
}
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 __int128>(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;
6357
#else
6458
#error Missing platform continuous time definition
6559
#endif
60+
*seconds = continuous.tv_sec;
61+
*nanoseconds = continuous.tv_nsec;
6662
return;
6763
}
6864
case swift_clock_id_suspending: {
69-
#if defined(__linux__) && HAS_TIME
7065
struct timespec suspending;
66+
#if defined(__linux__)
7167
clock_gettime(CLOCK_MONOTONIC, &suspending);
72-
*seconds = suspending.tv_sec;
73-
*nanoseconds = suspending.tv_nsec;
74-
#elif defined(__APPLE__) && HAS_TIME
75-
struct timespec suspending;
68+
#elif defined(__APPLE__)
7669
clock_gettime(CLOCK_UPTIME_RAW, &suspending);
77-
*seconds = suspending.tv_sec;
78-
*nanoseconds = suspending.tv_nsec;
79-
#elif defined(__wasi__) && HAS_TIME
80-
struct timespec suspending;
70+
#elif defined(__wasi__)
8171
clock_gettime(CLOCK_MONOTONIC, &suspending);
82-
*seconds = suspending.tv_sec;
83-
*nanoseconds = suspending.tv_nsec;
84-
#elif (defined(__OpenBSD__) || defined(__FreeBSD__)) && HAS_TIME
85-
struct timespec suspending;
72+
#elif (defined(__OpenBSD__) || defined(__FreeBSD__))
8673
clock_gettime(CLOCK_UPTIME, &suspending);
87-
*seconds = suspending.tv_sec;
88-
*nanoseconds = suspending.tv_nsec;
8974
#elif defined(_WIN32)
90-
LARGE_INTEGER freq;
91-
QueryPerformanceFrequency(&freq);
92-
LARGE_INTEGER count;
93-
QueryPerformanceCounter(&count);
94-
*seconds = count.QuadPart / freq.QuadPart;
95-
if (freq.QuadPart < 1000000000) {
96-
*nanoseconds =
97-
((count.QuadPart % freq.QuadPart) * 1000000000) / freq.QuadPart;
75+
// QueryUnbiasedInterruptTimePrecise() was added in Windows 10 and is, as
76+
// the name suggests, more precise than QueryUnbiasedInterruptTime().
77+
// Unfortunately, the symbol is not listed in any .lib file in the SDK and
78+
// must be looked up dynamically at runtime even if our minimum deployment
79+
// target is Windows 10.
80+
typedef decltype(QueryUnbiasedInterruptTimePrecise) *QueryUITP_FP;
81+
static QueryUITP_FP queryUITP = nullptr;
82+
static swift::once_t onceToken;
83+
swift::once(onceToken, [] {
84+
if (HMODULE hKernelBase = GetModuleHandleW(L"KernelBase.dll")) {
85+
queryUITP = reinterpret_cast<QueryUITP_FP>(
86+
GetProcAddress(hKernelBase, "QueryUnbiasedInterruptTimePrecise")
87+
);
88+
}
89+
});
90+
91+
// Call whichever API is available. Both output a value measured in 100ns
92+
// units. We must divide the output by 10,000,000 to get a value in
93+
// seconds and multiply the remainder by 100 to get nanoseconds.
94+
ULONGLONG unbiasedTime;
95+
if (queryUITP) {
96+
(* queryUITP)(&unbiasedTime);
9897
} else {
99-
*nanoseconds =
100-
(count.QuadPart % freq.QuadPart) * (1000000000.0 / freq.QuadPart);
98+
// Fall back to the older, less precise API.
99+
(void)QueryUnbiasedInterruptTime(&unbiasedTime);
101100
}
101+
suspending.tv_sec = unbiasedTime / 10'000'000;
102+
suspending.tv_nsec = (unbiasedTime % 10'000'000) * 100;
102103
#else
103104
#error Missing platform suspending time definition
104105
#endif
106+
*seconds = suspending.tv_sec;
107+
*nanoseconds = suspending.tv_nsec;
105108
return;
106109
}
107110
}
108-
abort(); // Invalid clock_id
111+
swift_Concurrency_fatalError(0, "Fatal error: invalid clock ID %d\n",
112+
clock_id);
109113
}
110114

111115
SWIFT_EXPORT_FROM(swift_Concurrency)
@@ -116,55 +120,46 @@ void swift_get_clock_res(
116120
swift_clock_id clock_id) {
117121
switch (clock_id) {
118122
case swift_clock_id_continuous: {
119-
#if defined(__linux__) && HAS_TIME
120123
struct timespec continuous;
124+
#if defined(__linux__)
121125
clock_getres(CLOCK_BOOTTIME, &continuous);
122-
*seconds = continuous.tv_sec;
123-
*nanoseconds = continuous.tv_nsec;
124-
#elif defined(__APPLE__) && HAS_TIME
125-
struct timespec continuous;
126+
#elif defined(__APPLE__)
126127
clock_getres(CLOCK_MONOTONIC_RAW, &continuous);
127-
*seconds = continuous.tv_sec;
128-
*nanoseconds = continuous.tv_nsec;
129-
#elif (defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__wasi__)) && HAS_TIME
130-
struct timespec continuous;
128+
#elif (defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__wasi__))
131129
clock_getres(CLOCK_MONOTONIC, &continuous);
132-
*seconds = continuous.tv_sec;
133-
*nanoseconds = continuous.tv_nsec;
134130
#elif defined(_WIN32)
135-
*seconds = 0;
136-
*nanoseconds = 1000;
131+
LARGE_INTEGER freq;
132+
QueryPerformanceFrequency(&freq);
133+
continuous.tv_sec = 0;
134+
continuous.tv_nsec = 1'000'000'000 / freq.QuadPart;
137135
#else
138136
#error Missing platform continuous time definition
139137
#endif
138+
*seconds = continuous.tv_sec;
139+
*nanoseconds = continuous.tv_nsec;
140140
return;
141141
}
142142
case swift_clock_id_suspending: {
143143
struct timespec suspending;
144-
#if defined(__linux__) && HAS_TIME
144+
#if defined(__linux__)
145145
clock_getres(CLOCK_MONOTONIC_RAW, &suspending);
146-
*seconds = suspending.tv_sec;
147-
*nanoseconds = suspending.tv_nsec;
148-
#elif defined(__APPLE__) && HAS_TIME
146+
#elif defined(__APPLE__)
149147
clock_getres(CLOCK_UPTIME_RAW, &suspending);
150-
*seconds = suspending.tv_sec;
151-
*nanoseconds = suspending.tv_nsec;
152-
#elif defined(__wasi__) && HAS_TIME
148+
#elif defined(__wasi__)
153149
clock_getres(CLOCK_MONOTONIC, &suspending);
154-
*seconds = suspending.tv_sec;
155-
*nanoseconds = suspending.tv_nsec;
156-
#elif (defined(__OpenBSD__) || defined(__FreeBSD__)) && HAS_TIME
150+
#elif (defined(__OpenBSD__) || defined(__FreeBSD__))
157151
clock_getres(CLOCK_UPTIME, &suspending);
158-
*seconds = suspending.tv_sec;
159-
*nanoseconds = suspending.tv_nsec;
160152
#elif defined(_WIN32)
161-
*seconds = 0;
162-
*nanoseconds = 1000;
153+
suspending.tv_sec = 0;
154+
suspending.tv_nsec = 100;
163155
#else
164156
#error Missing platform suspending time definition
165157
#endif
158+
*seconds = suspending.tv_sec;
159+
*nanoseconds = suspending.tv_nsec;
166160
return;
167161
}
168162
}
169-
abort(); // Invalid clock_id
163+
swift_Concurrency_fatalError(0, "Fatal error: invalid clock ID %d\n",
164+
clock_id);
170165
}

0 commit comments

Comments
 (0)