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