1 /*
2 Copyright (c) 2007-2016 Contributors as noted in the AUTHORS file
3
4 This file is part of libzmq, the ZeroMQ core engine in C++.
5
6 libzmq is free software; you can redistribute it and/or modify it under
7 the terms of the GNU Lesser General Public License (LGPL) as published
8 by the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
10
11 As a special exception, the Contributors give you permission to link
12 this library with independent modules to produce an executable,
13 regardless of the license terms of these independent modules, and to
14 copy and distribute the resulting executable under terms of your choice,
15 provided that you also meet, for each linked independent module, the
16 terms and conditions of the license of that module. An independent
17 module is a module which is not derived from or based on this library.
18 If you modify this library, you must extend this exception to your
19 version of the library.
20
21 libzmq is distributed in the hope that it will be useful, but WITHOUT
22 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
23 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
24 License for more details.
25
26 You should have received a copy of the GNU Lesser General Public License
27 along with this program. If not, see <http://www.gnu.org/licenses/>.
28 */
29
30 #include "precompiled.hpp"
31 #include "clock.hpp"
32 #include "likely.hpp"
33 #include "config.hpp"
34 #include "err.hpp"
35 #include "mutex.hpp"
36
37 #include <stddef.h>
38
39 #if defined _MSC_VER
40 #if defined _WIN32_WCE
41 #include <cmnintrin.h>
42 #else
43 #include <intrin.h>
44 #if defined(_M_ARM) || defined(_M_ARM64)
45 #include <arm_neon.h>
46 #endif
47 #endif
48 #endif
49
50 #if !defined ZMQ_HAVE_WINDOWS
51 #include <sys/time.h>
52 #endif
53
54 #if defined HAVE_CLOCK_GETTIME || defined HAVE_GETHRTIME
55 #include <time.h>
56 #endif
57
58 #if defined ZMQ_HAVE_VXWORKS
59 #include "timers.h"
60 #endif
61
62 #if defined ZMQ_HAVE_OSX
alt_clock_gettime(int clock_id,timespec * ts)63 int alt_clock_gettime (int clock_id, timespec *ts)
64 {
65 clock_serv_t cclock;
66 mach_timespec_t mts;
67 host_get_clock_service (mach_host_self (), clock_id, &cclock);
68 clock_get_time (cclock, &mts);
69 mach_port_deallocate (mach_task_self (), cclock);
70 ts->tv_sec = mts.tv_sec;
71 ts->tv_nsec = mts.tv_nsec;
72 return 0;
73 }
74 #endif
75
76 #ifdef ZMQ_HAVE_WINDOWS
77 typedef ULONGLONG (*f_compatible_get_tick_count64) ();
78
79 static zmq::mutex_t compatible_get_tick_count64_mutex;
80
compatible_get_tick_count64()81 ULONGLONG compatible_get_tick_count64 ()
82 {
83 #ifdef ZMQ_HAVE_WINDOWS_UWP
84 const ULONGLONG result = ::GetTickCount64 ();
85 return result;
86 #else
87 zmq::scoped_lock_t locker (compatible_get_tick_count64_mutex);
88
89 static DWORD s_wrap = 0;
90 static DWORD s_last_tick = 0;
91 const DWORD current_tick = ::GetTickCount ();
92
93 if (current_tick < s_last_tick)
94 ++s_wrap;
95
96 s_last_tick = current_tick;
97 const ULONGLONG result = (static_cast<ULONGLONG> (s_wrap) << 32)
98 + static_cast<ULONGLONG> (current_tick);
99
100 return result;
101 #endif
102 }
103
init_compatible_get_tick_count64()104 f_compatible_get_tick_count64 init_compatible_get_tick_count64 ()
105 {
106 f_compatible_get_tick_count64 func = NULL;
107 #if !defined ZMQ_HAVE_WINDOWS_UWP
108
109 const HMODULE module = ::LoadLibraryA ("Kernel32.dll");
110 if (module != NULL)
111 func = reinterpret_cast<f_compatible_get_tick_count64> (
112 ::GetProcAddress (module, "GetTickCount64"));
113 #endif
114 if (func == NULL)
115 func = compatible_get_tick_count64;
116
117 #if !defined ZMQ_HAVE_WINDOWS_UWP
118 if (module != NULL)
119 ::FreeLibrary (module);
120 #endif
121
122 return func;
123 }
124
125 static f_compatible_get_tick_count64 my_get_tick_count64 =
126 init_compatible_get_tick_count64 ();
127 #endif
128
129 const uint64_t usecs_per_msec = 1000;
130 const uint64_t usecs_per_sec = 1000000;
131 const uint64_t nsecs_per_usec = 1000;
132
clock_t()133 zmq::clock_t::clock_t () :
134 _last_tsc (rdtsc ()),
135 #ifdef ZMQ_HAVE_WINDOWS
136 _last_time (static_cast<uint64_t> ((*my_get_tick_count64) ()))
137 #else
138 _last_time (now_us () / usecs_per_msec)
139 #endif
140 {
141 }
142
now_us()143 uint64_t zmq::clock_t::now_us ()
144 {
145 #if defined ZMQ_HAVE_WINDOWS
146
147 // Get the high resolution counter's accuracy.
148 // While QueryPerformanceFrequency only needs to be called once, since its
149 // value does not change during runtime, we query it here since this is a
150 // static function. It might make sense to cache it, though.
151 LARGE_INTEGER ticks_per_second;
152 QueryPerformanceFrequency (&ticks_per_second);
153
154 // What time is it?
155 LARGE_INTEGER tick;
156 QueryPerformanceCounter (&tick);
157
158 // Convert the tick number into the number of seconds
159 // since the system was started.
160 const double ticks_div =
161 static_cast<double> (ticks_per_second.QuadPart) / usecs_per_sec;
162 return static_cast<uint64_t> (tick.QuadPart / ticks_div);
163
164 #elif defined HAVE_CLOCK_GETTIME \
165 && (defined CLOCK_MONOTONIC || defined ZMQ_HAVE_VXWORKS)
166
167 // Use POSIX clock_gettime function to get precise monotonic time.
168 struct timespec tv;
169
170 #if defined ZMQ_HAVE_OSX \
171 && __MAC_OS_X_VERSION_MIN_REQUIRED < 101200 // less than macOS 10.12
172 int rc = alt_clock_gettime (SYSTEM_CLOCK, &tv);
173 #else
174 int rc = clock_gettime (CLOCK_MONOTONIC, &tv);
175 #endif
176 // Fix case where system has clock_gettime but CLOCK_MONOTONIC is not supported.
177 // This should be a configuration check, but I looked into it and writing an
178 // AC_FUNC_CLOCK_MONOTONIC seems beyond my powers.
179 if (rc != 0) {
180 #ifndef ZMQ_HAVE_VXWORKS
181 // Use POSIX gettimeofday function to get precise time.
182 struct timeval tv;
183 int rc = gettimeofday (&tv, NULL);
184 errno_assert (rc == 0);
185 return tv.tv_sec * usecs_per_sec + tv.tv_usec;
186 #endif
187 }
188 return tv.tv_sec * usecs_per_sec + tv.tv_nsec / nsecs_per_usec;
189
190 #elif defined HAVE_GETHRTIME
191
192 return gethrtime () / nsecs_per_usec;
193
194 #else
195
196 // Use POSIX gettimeofday function to get precise time.
197 struct timeval tv;
198 int rc = gettimeofday (&tv, NULL);
199 errno_assert (rc == 0);
200 return tv.tv_sec * usecs_per_sec + tv.tv_usec;
201
202 #endif
203 }
204
now_ms()205 uint64_t zmq::clock_t::now_ms ()
206 {
207 const uint64_t tsc = rdtsc ();
208
209 // If TSC is not supported, get precise time and chop off the microseconds.
210 if (!tsc) {
211 #ifdef ZMQ_HAVE_WINDOWS
212 // Under Windows, now_us is not so reliable since QueryPerformanceCounter
213 // does not guarantee that it will use a hardware that offers a monotonic timer.
214 // So, lets use GetTickCount when GetTickCount64 is not available with an workaround
215 // to its 32 bit limitation.
216 return static_cast<uint64_t> ((*my_get_tick_count64) ());
217 #else
218 return now_us () / usecs_per_msec;
219 #endif
220 }
221
222 // If TSC haven't jumped back (in case of migration to a different
223 // CPU core) and if not too much time elapsed since last measurement,
224 // we can return cached time value.
225 if (likely (tsc - _last_tsc <= (clock_precision / 2) && tsc >= _last_tsc))
226 return _last_time;
227
228 _last_tsc = tsc;
229 #ifdef ZMQ_HAVE_WINDOWS
230 _last_time = static_cast<uint64_t> ((*my_get_tick_count64) ());
231 #else
232 _last_time = now_us () / usecs_per_msec;
233 #endif
234 return _last_time;
235 }
236
rdtsc()237 uint64_t zmq::clock_t::rdtsc ()
238 {
239 #if (defined _MSC_VER && (defined _M_IX86 || defined _M_X64))
240 return __rdtsc ();
241 #elif defined(_MSC_VER) && defined(_M_ARM) // NC => added for windows ARM
242 return __rdpmccntr64 ();
243 #elif defined(_MSC_VER) && defined(_M_ARM64) // NC => added for windows ARM64
244 //return __rdpmccntr64 ();
245 //return __rdtscp (nullptr);
246 // todo: find proper implementation for ARM64
247 static uint64_t snCounter = 0;
248 return ++snCounter;
249 #elif (defined __GNUC__ && (defined __i386__ || defined __x86_64__))
250 uint32_t low, high;
251 __asm__ volatile("rdtsc" : "=a"(low), "=d"(high));
252 return static_cast<uint64_t> (high) << 32 | low;
253 #elif (defined __SUNPRO_CC && (__SUNPRO_CC >= 0x5100) \
254 && (defined __i386 || defined __amd64 || defined __x86_64))
255 union
256 {
257 uint64_t u64val;
258 uint32_t u32val[2];
259 } tsc;
260 asm("rdtsc" : "=a"(tsc.u32val[0]), "=d"(tsc.u32val[1]));
261 return tsc.u64val;
262 #elif defined(__s390__)
263 uint64_t tsc;
264 asm("\tstck\t%0\n" : "=Q"(tsc) : : "cc");
265 return tsc;
266 #else
267 struct timespec ts;
268 #if defined ZMQ_HAVE_OSX \
269 && __MAC_OS_X_VERSION_MIN_REQUIRED < 101200 // less than macOS 10.12
270 alt_clock_gettime (SYSTEM_CLOCK, &ts);
271 #else
272 clock_gettime (CLOCK_MONOTONIC, &ts);
273 #endif
274 return static_cast<uint64_t> (ts.tv_sec) * nsecs_per_usec * usecs_per_sec
275 + ts.tv_nsec;
276 #endif
277 }
278