/src/libzmq/src/clock.cpp
Line | Count | Source |
1 | | /* SPDX-License-Identifier: MPL-2.0 */ |
2 | | |
3 | | #include "precompiled.hpp" |
4 | | #include "clock.hpp" |
5 | | #include "likely.hpp" |
6 | | #include "config.hpp" |
7 | | #include "err.hpp" |
8 | | #include "mutex.hpp" |
9 | | |
10 | | #include <stddef.h> |
11 | | |
12 | | #if defined _MSC_VER |
13 | | #if defined _WIN32_WCE |
14 | | #include <cmnintrin.h> |
15 | | #else |
16 | | #include <intrin.h> |
17 | | #if defined(_M_ARM) || defined(_M_ARM64) |
18 | | #include <arm_neon.h> |
19 | | #endif |
20 | | #endif |
21 | | #endif |
22 | | |
23 | | #if !defined ZMQ_HAVE_WINDOWS |
24 | | #include <sys/time.h> |
25 | | #endif |
26 | | |
27 | | #if defined HAVE_CLOCK_GETTIME || defined HAVE_GETHRTIME |
28 | | #include <time.h> |
29 | | #endif |
30 | | |
31 | | #if defined ZMQ_HAVE_VXWORKS |
32 | | #include "timers.h" |
33 | | #endif |
34 | | |
35 | | #if defined ZMQ_HAVE_OSX |
36 | | int alt_clock_gettime (int clock_id, timespec *ts) |
37 | | { |
38 | | clock_serv_t cclock; |
39 | | mach_timespec_t mts; |
40 | | host_get_clock_service (mach_host_self (), clock_id, &cclock); |
41 | | clock_get_time (cclock, &mts); |
42 | | mach_port_deallocate (mach_task_self (), cclock); |
43 | | ts->tv_sec = mts.tv_sec; |
44 | | ts->tv_nsec = mts.tv_nsec; |
45 | | return 0; |
46 | | } |
47 | | #endif |
48 | | |
49 | | #ifdef ZMQ_HAVE_WINDOWS |
50 | | typedef ULONGLONG (*f_compatible_get_tick_count64) (); |
51 | | |
52 | | static zmq::mutex_t compatible_get_tick_count64_mutex; |
53 | | |
54 | | ULONGLONG compatible_get_tick_count64 () |
55 | | { |
56 | | #ifdef ZMQ_HAVE_WINDOWS_UWP |
57 | | const ULONGLONG result = ::GetTickCount64 (); |
58 | | return result; |
59 | | #else |
60 | | zmq::scoped_lock_t locker (compatible_get_tick_count64_mutex); |
61 | | |
62 | | static DWORD s_wrap = 0; |
63 | | static DWORD s_last_tick = 0; |
64 | | const DWORD current_tick = ::GetTickCount (); |
65 | | |
66 | | if (current_tick < s_last_tick) |
67 | | ++s_wrap; |
68 | | |
69 | | s_last_tick = current_tick; |
70 | | const ULONGLONG result = (static_cast<ULONGLONG> (s_wrap) << 32) |
71 | | + static_cast<ULONGLONG> (current_tick); |
72 | | |
73 | | return result; |
74 | | #endif |
75 | | } |
76 | | |
77 | | f_compatible_get_tick_count64 init_compatible_get_tick_count64 () |
78 | | { |
79 | | f_compatible_get_tick_count64 func = NULL; |
80 | | #if !defined ZMQ_HAVE_WINDOWS_UWP |
81 | | |
82 | | const HMODULE module = ::LoadLibraryA ("Kernel32.dll"); |
83 | | if (module != NULL) |
84 | | func = reinterpret_cast<f_compatible_get_tick_count64> ( |
85 | | ::GetProcAddress (module, "GetTickCount64")); |
86 | | #endif |
87 | | if (func == NULL) |
88 | | func = compatible_get_tick_count64; |
89 | | |
90 | | #if !defined ZMQ_HAVE_WINDOWS_UWP |
91 | | if (module != NULL) |
92 | | ::FreeLibrary (module); |
93 | | #endif |
94 | | |
95 | | return func; |
96 | | } |
97 | | |
98 | | static f_compatible_get_tick_count64 my_get_tick_count64 = |
99 | | init_compatible_get_tick_count64 (); |
100 | | #endif |
101 | | |
102 | | #ifndef ZMQ_HAVE_WINDOWS |
103 | | const uint64_t usecs_per_msec = 1000; |
104 | | const uint64_t nsecs_per_usec = 1000; |
105 | | #endif |
106 | | const uint64_t usecs_per_sec = 1000000; |
107 | | |
108 | | zmq::clock_t::clock_t () : |
109 | 5.13k | _last_tsc (rdtsc ()), |
110 | | #ifdef ZMQ_HAVE_WINDOWS |
111 | | _last_time (static_cast<uint64_t> ((*my_get_tick_count64) ())) |
112 | | #else |
113 | 5.13k | _last_time (now_us () / usecs_per_msec) |
114 | | #endif |
115 | 5.13k | { |
116 | 5.13k | } |
117 | | |
118 | | uint64_t zmq::clock_t::now_us () |
119 | 5.39k | { |
120 | | #if defined ZMQ_HAVE_WINDOWS |
121 | | |
122 | | // Get the high resolution counter's accuracy. |
123 | | // While QueryPerformanceFrequency only needs to be called once, since its |
124 | | // value does not change during runtime, we query it here since this is a |
125 | | // static function. It might make sense to cache it, though. |
126 | | LARGE_INTEGER ticks_per_second; |
127 | | QueryPerformanceFrequency (&ticks_per_second); |
128 | | |
129 | | // What time is it? |
130 | | LARGE_INTEGER tick; |
131 | | QueryPerformanceCounter (&tick); |
132 | | |
133 | | // Convert the tick number into the number of seconds |
134 | | // since the system was started. |
135 | | const double ticks_div = |
136 | | static_cast<double> (ticks_per_second.QuadPart) / usecs_per_sec; |
137 | | return static_cast<uint64_t> (tick.QuadPart / ticks_div); |
138 | | |
139 | | #elif defined HAVE_CLOCK_GETTIME \ |
140 | | && (defined CLOCK_MONOTONIC || defined ZMQ_HAVE_VXWORKS) |
141 | | |
142 | | // Use POSIX clock_gettime function to get precise monotonic time. |
143 | 5.39k | struct timespec tv; |
144 | | |
145 | | #if defined ZMQ_HAVE_OSX \ |
146 | | && __MAC_OS_X_VERSION_MIN_REQUIRED < 101200 // less than macOS 10.12 |
147 | | int rc = alt_clock_gettime (SYSTEM_CLOCK, &tv); |
148 | | #else |
149 | 5.39k | int rc = clock_gettime (CLOCK_MONOTONIC, &tv); |
150 | 5.39k | #endif |
151 | | // Fix case where system has clock_gettime but CLOCK_MONOTONIC is not supported. |
152 | | // This should be a configuration check, but I looked into it and writing an |
153 | | // AC_FUNC_CLOCK_MONOTONIC seems beyond my powers. |
154 | 5.39k | if (rc != 0) { |
155 | 0 | #ifndef ZMQ_HAVE_VXWORKS |
156 | | // Use POSIX gettimeofday function to get precise time. |
157 | 0 | struct timeval tv; |
158 | 0 | int rc = gettimeofday (&tv, NULL); |
159 | 0 | errno_assert (rc == 0); |
160 | 0 | return tv.tv_sec * usecs_per_sec + tv.tv_usec; |
161 | 0 | #endif |
162 | 0 | } |
163 | 5.39k | return tv.tv_sec * usecs_per_sec + tv.tv_nsec / nsecs_per_usec; |
164 | | |
165 | | #elif defined HAVE_GETHRTIME |
166 | | |
167 | | return gethrtime () / nsecs_per_usec; |
168 | | |
169 | | #else |
170 | | |
171 | | LIBZMQ_UNUSED (nsecs_per_usec); |
172 | | // Use POSIX gettimeofday function to get precise time. |
173 | | struct timeval tv; |
174 | | int rc = gettimeofday (&tv, NULL); |
175 | | errno_assert (rc == 0); |
176 | | return tv.tv_sec * usecs_per_sec + tv.tv_usec; |
177 | | |
178 | | #endif |
179 | 5.39k | } |
180 | | |
181 | | uint64_t zmq::clock_t::now_ms () |
182 | 996 | { |
183 | 996 | const uint64_t tsc = rdtsc (); |
184 | | |
185 | | // If TSC is not supported, get precise time and chop off the microseconds. |
186 | 996 | if (!tsc) { |
187 | | #ifdef ZMQ_HAVE_WINDOWS |
188 | | // Under Windows, now_us is not so reliable since QueryPerformanceCounter |
189 | | // does not guarantee that it will use a hardware that offers a monotonic timer. |
190 | | // So, lets use GetTickCount when GetTickCount64 is not available with an workaround |
191 | | // to its 32 bit limitation. |
192 | | return static_cast<uint64_t> ((*my_get_tick_count64) ()); |
193 | | #else |
194 | 0 | return now_us () / usecs_per_msec; |
195 | 0 | #endif |
196 | 0 | } |
197 | | |
198 | | // If TSC haven't jumped back (in case of migration to a different |
199 | | // CPU core) and if not too much time elapsed since last measurement, |
200 | | // we can return cached time value. |
201 | 996 | if (likely (tsc - _last_tsc <= (clock_precision / 2) && tsc >= _last_tsc)) |
202 | 734 | return _last_time; |
203 | | |
204 | 262 | _last_tsc = tsc; |
205 | | #ifdef ZMQ_HAVE_WINDOWS |
206 | | _last_time = static_cast<uint64_t> ((*my_get_tick_count64) ()); |
207 | | #else |
208 | 262 | _last_time = now_us () / usecs_per_msec; |
209 | 262 | #endif |
210 | 262 | return _last_time; |
211 | 996 | } |
212 | | |
213 | | uint64_t zmq::clock_t::rdtsc () |
214 | 9.73k | { |
215 | | #if (defined _MSC_VER && (defined _M_IX86 || defined _M_X64)) |
216 | | return __rdtsc (); |
217 | | #elif defined(_MSC_VER) && defined(_M_ARM) // NC => added for windows ARM |
218 | | return __rdpmccntr64 (); |
219 | | #elif defined(_MSC_VER) && defined(_M_ARM64) // NC => added for windows ARM64 |
220 | | const int64_t pmccntr_el0 = (((3 & 1) << 14) | // op0 |
221 | | ((3 & 7) << 11) | // op1 |
222 | | ((9 & 15) << 7) | // crn |
223 | | ((13 & 15) << 3) | // crm |
224 | | ((0 & 7) << 0)); // op2 |
225 | | return _ReadStatusReg (pmccntr_el0); |
226 | | #elif (defined(_WIN32) && defined(__GNUC__) && defined(__aarch64__)) |
227 | | uint64_t val; |
228 | | __asm__ volatile ("mrs %0, pmccntr_el0" : "=r"(val)); |
229 | | return val; |
230 | | #elif (defined __GNUC__ && (defined __i386__ || defined __x86_64__)) |
231 | | uint32_t low, high; |
232 | 9.73k | __asm__ volatile ("rdtsc" : "=a"(low), "=d"(high)); |
233 | 9.73k | return static_cast<uint64_t> (high) << 32 | low; |
234 | | #elif (defined __SUNPRO_CC && (__SUNPRO_CC >= 0x5100) \ |
235 | | && (defined __i386 || defined __amd64 || defined __x86_64)) |
236 | | union |
237 | | { |
238 | | uint64_t u64val; |
239 | | uint32_t u32val[2]; |
240 | | } tsc; |
241 | | asm ("rdtsc" : "=a"(tsc.u32val[0]), "=d"(tsc.u32val[1])); |
242 | | return tsc.u64val; |
243 | | #elif defined(__s390__) |
244 | | uint64_t tsc; |
245 | | asm ("\tstck\t%0\n" : "=Q"(tsc) : : "cc"); |
246 | | return tsc; |
247 | | #else |
248 | | struct timespec ts; |
249 | | #if defined ZMQ_HAVE_OSX \ |
250 | | && __MAC_OS_X_VERSION_MIN_REQUIRED < 101200 // less than macOS 10.12 |
251 | | alt_clock_gettime (SYSTEM_CLOCK, &ts); |
252 | | #else |
253 | | clock_gettime (CLOCK_MONOTONIC, &ts); |
254 | | #endif |
255 | | return static_cast<uint64_t> (ts.tv_sec) * nsecs_per_usec * usecs_per_sec |
256 | | + ts.tv_nsec; |
257 | | #endif |
258 | 9.73k | } |