11
11
// ===----------------------------------------------------------------------===//
12
12
13
13
#include " swift/Runtime/Concurrency.h"
14
+ #include " swift/Runtime/Once.h"
14
15
15
- #if __has_include(<time.h>)
16
- #define HAS_TIME 1
17
16
#include < time.h>
18
- #endif
19
17
#if defined(_WIN32)
20
18
#define WIN32_LEAN_AND_MEAN
21
19
#define NOMINMAX
22
20
#include < Windows.h>
21
+ #include < realtimeapiset.h>
23
22
#endif
24
23
24
+ #include " Error.h"
25
+
25
26
using namespace swift ;
26
27
27
28
SWIFT_EXPORT_FROM (swift_Concurrency)
@@ -32,80 +33,83 @@ void swift_get_time(
32
33
swift_clock_id clock_id) {
33
34
switch (clock_id) {
34
35
case swift_clock_id_continuous: {
35
- #if defined(__linux__) && HAS_TIME
36
36
struct timespec continuous;
37
+ #if defined(__linux__)
37
38
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__)
42
40
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__))
47
42
clock_gettime (CLOCK_MONOTONIC, &continuous);
48
- *seconds = continuous.tv_sec ;
49
- *nanoseconds = continuous.tv_nsec ;
50
43
#elif defined(_WIN32)
51
44
LARGE_INTEGER freq;
52
45
QueryPerformanceFrequency (&freq);
53
46
LARGE_INTEGER count;
54
47
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 ;
63
57
#else
64
58
#error Missing platform continuous time definition
65
59
#endif
60
+ *seconds = continuous.tv_sec ;
61
+ *nanoseconds = continuous.tv_nsec ;
66
62
return ;
67
63
}
68
64
case swift_clock_id_suspending: {
69
- #if defined(__linux__) && HAS_TIME
70
65
struct timespec suspending;
66
+ #if defined(__linux__)
71
67
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__)
76
69
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__)
81
71
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__))
86
73
clock_gettime (CLOCK_UPTIME, &suspending);
87
- *seconds = suspending.tv_sec ;
88
- *nanoseconds = suspending.tv_nsec ;
89
74
#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);
98
97
} 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 );
101
100
}
101
+ suspending.tv_sec = unbiasedTime / 10'000'000 ;
102
+ suspending.tv_nsec = (unbiasedTime % 10'000'000 ) * 100 ;
102
103
#else
103
104
#error Missing platform suspending time definition
104
105
#endif
106
+ *seconds = suspending.tv_sec ;
107
+ *nanoseconds = suspending.tv_nsec ;
105
108
return ;
106
109
}
107
110
}
108
- abort (); // Invalid clock_id
111
+ swift_Concurrency_fatalError (0 , " Fatal error: invalid clock ID %d\n " ,
112
+ clock_id);
109
113
}
110
114
111
115
SWIFT_EXPORT_FROM (swift_Concurrency)
@@ -116,55 +120,46 @@ void swift_get_clock_res(
116
120
swift_clock_id clock_id) {
117
121
switch (clock_id) {
118
122
case swift_clock_id_continuous: {
119
- #if defined(__linux__) && HAS_TIME
120
123
struct timespec continuous;
124
+ #if defined(__linux__)
121
125
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__)
126
127
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__))
131
129
clock_getres (CLOCK_MONOTONIC, &continuous);
132
- *seconds = continuous.tv_sec ;
133
- *nanoseconds = continuous.tv_nsec ;
134
130
#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 ;
137
135
#else
138
136
#error Missing platform continuous time definition
139
137
#endif
138
+ *seconds = continuous.tv_sec ;
139
+ *nanoseconds = continuous.tv_nsec ;
140
140
return ;
141
141
}
142
142
case swift_clock_id_suspending: {
143
143
struct timespec suspending;
144
- #if defined(__linux__) && HAS_TIME
144
+ #if defined(__linux__)
145
145
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__)
149
147
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__)
153
149
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__))
157
151
clock_getres (CLOCK_UPTIME, &suspending);
158
- *seconds = suspending.tv_sec ;
159
- *nanoseconds = suspending.tv_nsec ;
160
152
#elif defined(_WIN32)
161
- *seconds = 0 ;
162
- *nanoseconds = 1000 ;
153
+ suspending. tv_sec = 0 ;
154
+ suspending. tv_nsec = 100 ;
163
155
#else
164
156
#error Missing platform suspending time definition
165
157
#endif
158
+ *seconds = suspending.tv_sec ;
159
+ *nanoseconds = suspending.tv_nsec ;
166
160
return ;
167
161
}
168
162
}
169
- abort (); // Invalid clock_id
163
+ swift_Concurrency_fatalError (0 , " Fatal error: invalid clock ID %d\n " ,
164
+ clock_id);
170
165
}
0 commit comments