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+
2526using namespace swift ;
2627
2728SWIFT_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
111115SWIFT_EXPORT_FROM (swift_Concurrency)
@@ -116,55 +120,46 @@ void swift_get_clock_res(
116120 swift_clock_id clock_id) {
117121switch (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