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