1/*
2 * Copyright (C) 2006-2019 Apple Inc. All rights reserved.
3 * Copyright (C) 2008 Google Inc. All rights reserved.
4 * Copyright (C) 2007-2009 Torch Mobile, Inc.
5 * Copyright (C) 2008 Cameron Zwarich <[email protected]>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are
9 * met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * * Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following disclaimer
15 * in the documentation and/or other materials provided with the
16 * distribution.
17 * * Neither the name of Google Inc. nor the names of its
18 * contributors may be used to endorse or promote products derived from
19 * this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34#include "config.h"
35#include <wtf/MonotonicTime.h>
36
37#include <time.h>
38#include <wtf/WallTime.h>
39
40#if OS(DARWIN)
41#include <mach/mach.h>
42#include <mach/mach_time.h>
43#include <mutex>
44#include <sys/time.h>
45#elif OS(WINDOWS)
46
47// Windows is first since we want to use hires timers, despite USE(CF)
48// being defined.
49// If defined, WIN32_LEAN_AND_MEAN disables timeBeginPeriod/timeEndPeriod.
50#undef WIN32_LEAN_AND_MEAN
51#include <windows.h>
52#include <math.h>
53#include <stdint.h>
54#else
55#include <sys/time.h>
56#endif
57
58#if OS(FUCHSIA)
59#include <zircon/syscalls.h>
60#endif
61
62#if USE(GLIB)
63#include <glib.h>
64#endif
65
66namespace WTF {
67
68#if OS(WINDOWS)
69
70// Number of 100 nanosecond between January 1, 1601 and January 1, 1970.
71static constexpr ULONGLONG epochBias = 116444736000000000ULL;
72static constexpr double hundredsOfNanosecondsPerMillisecond = 10000;
73
74static double lowResUTCTime()
75{
76 FILETIME fileTime;
77
78 GetSystemTimeAsFileTime(&fileTime);
79
80 // As per Windows documentation for FILETIME, copy the resulting FILETIME structure to a
81 // ULARGE_INTEGER structure using memcpy (using memcpy instead of direct assignment can
82 // prevent alignment faults on 64-bit Windows).
83
84 ULARGE_INTEGER dateTime;
85 memcpy(&dateTime, &fileTime, sizeof(dateTime));
86
87 // Windows file times are in 100s of nanoseconds.
88 return (dateTime.QuadPart - epochBias) / hundredsOfNanosecondsPerMillisecond;
89}
90
91#if USE(QUERY_PERFORMANCE_COUNTER)
92
93static LARGE_INTEGER qpcFrequency;
94static bool syncedTime;
95
96static double highResUpTime()
97{
98 // We use QPC, but only after sanity checking its result, due to bugs:
99 // http://support.microsoft.com/kb/274323
100 // http://support.microsoft.com/kb/895980
101 // http://msdn.microsoft.com/en-us/library/ms644904.aspx ("...you can get different results on different processors due to bugs in the basic input/output system (BIOS) or the hardware abstraction layer (HAL)."
102
103 static LARGE_INTEGER qpcLast;
104 static DWORD tickCountLast;
105 static bool inited;
106
107 LARGE_INTEGER qpc;
108 QueryPerformanceCounter(&qpc);
109#if defined(_M_IX86) || defined(__i386__)
110 DWORD tickCount = GetTickCount();
111#else
112 ULONGLONG tickCount = GetTickCount64();
113#endif
114
115 if (inited) {
116 __int64 qpcElapsed = ((qpc.QuadPart - qpcLast.QuadPart) * 1000) / qpcFrequency.QuadPart;
117 __int64 tickCountElapsed;
118 if (tickCount >= tickCountLast)
119 tickCountElapsed = (tickCount - tickCountLast);
120 else {
121#if COMPILER(MINGW)
122 __int64 tickCountLarge = tickCount + 0x100000000ULL;
123#else
124 __int64 tickCountLarge = tickCount + 0x100000000I64;
125#endif
126 tickCountElapsed = tickCountLarge - tickCountLast;
127 }
128
129 // force a re-sync if QueryPerformanceCounter differs from GetTickCount by more than 500ms.
130 // (500ms value is from http://support.microsoft.com/kb/274323)
131 __int64 diff = tickCountElapsed - qpcElapsed;
132 if (diff > 500 || diff < -500)
133 syncedTime = false;
134 } else
135 inited = true;
136
137 qpcLast = qpc;
138 tickCountLast = tickCount;
139
140 return (1000.0 * qpc.QuadPart) / static_cast<double>(qpcFrequency.QuadPart);
141}
142
143static bool qpcAvailable()
144{
145 static bool available;
146 static bool checked;
147
148 if (checked)
149 return available;
150
151 available = QueryPerformanceFrequency(&qpcFrequency);
152 checked = true;
153 return available;
154}
155
156static inline double currentTime()
157{
158 // Use a combination of ftime and QueryPerformanceCounter.
159 // ftime returns the information we want, but doesn't have sufficient resolution.
160 // QueryPerformanceCounter has high resolution, but is only usable to measure time intervals.
161 // To combine them, we call ftime and QueryPerformanceCounter initially. Later calls will use QueryPerformanceCounter
162 // by itself, adding the delta to the saved ftime. We periodically re-sync to correct for drift.
163 static double syncLowResUTCTime;
164 static double syncHighResUpTime;
165 static double lastUTCTime;
166
167 double lowResTime = lowResUTCTime();
168
169 if (!qpcAvailable())
170 return lowResTime / 1000.0;
171
172 double highResTime = highResUpTime();
173
174 if (!syncedTime) {
175 timeBeginPeriod(1); // increase time resolution around low-res time getter
176 syncLowResUTCTime = lowResTime = lowResUTCTime();
177 timeEndPeriod(1); // restore time resolution
178 syncHighResUpTime = highResTime;
179 syncedTime = true;
180 }
181
182 double highResElapsed = highResTime - syncHighResUpTime;
183 double utc = syncLowResUTCTime + highResElapsed;
184
185 // force a clock re-sync if we've drifted
186 double lowResElapsed = lowResTime - syncLowResUTCTime;
187 const double maximumAllowedDriftMsec = 15.625 * 2.0; // 2x the typical low-res accuracy
188 if (fabs(highResElapsed - lowResElapsed) > maximumAllowedDriftMsec)
189 syncedTime = false;
190
191 // make sure time doesn't run backwards (only correct if difference is < 2 seconds, since DST or clock changes could occur)
192 const double backwardTimeLimit = 2000.0;
193 if (utc < lastUTCTime && (lastUTCTime - utc) < backwardTimeLimit)
194 return lastUTCTime / 1000.0;
195 lastUTCTime = utc;
196 return utc / 1000.0;
197}
198
199#else
200
201static inline double currentTime()
202{
203 static bool init = false;
204 static double lastTime;
205 static DWORD lastTickCount;
206 if (!init) {
207 lastTime = lowResUTCTime();
208 lastTickCount = GetTickCount();
209 init = true;
210 return lastTime;
211 }
212
213 DWORD tickCountNow = GetTickCount();
214 DWORD elapsed = tickCountNow - lastTickCount;
215 double timeNow = lastTime + (double)elapsed / 1000.;
216 if (elapsed >= 0x7FFFFFFF) {
217 lastTime = timeNow;
218 lastTickCount = tickCountNow;
219 }
220 return timeNow;
221}
222
223#endif // USE(QUERY_PERFORMANCE_COUNTER)
224
225#elif USE(GLIB)
226
227// Note: GTK on Windows will pick up the PLATFORM(WIN) implementation above which provides
228// better accuracy compared with Windows implementation of g_get_current_time:
229// (http://www.google.com/codesearch/p?hl=en#HHnNRjks1t0/glib-2.5.2/glib/gmain.c&q=g_get_current_time).
230// Non-Windows GTK builds could use gettimeofday() directly but for the sake of consistency lets use GTK function.
231static inline double currentTime()
232{
233 GTimeVal now;
234 g_get_current_time(&now);
235 return static_cast<double>(now.tv_sec) + static_cast<double>(now.tv_usec / 1000000.0);
236}
237
238#else
239
240static inline double currentTime()
241{
242 struct timeval now;
243 gettimeofday(&now, 0);
244 return now.tv_sec + now.tv_usec / 1000000.0;
245}
246
247#endif
248
249WallTime WallTime::now()
250{
251 return fromRawSeconds(currentTime());
252}
253
254MonotonicTime MonotonicTime::now()
255{
256#if USE(GLIB)
257 return fromRawSeconds(static_cast<double>(g_get_monotonic_time() / 1000000.0));
258#elif OS(DARWIN)
259 // Based on listing #2 from Apple QA 1398, but modified to be thread-safe.
260 static mach_timebase_info_data_t timebaseInfo;
261 static std::once_flag initializeTimerOnceFlag;
262 std::call_once(initializeTimerOnceFlag, [] {
263 kern_return_t kr = mach_timebase_info(&timebaseInfo);
264 ASSERT_UNUSED(kr, kr == KERN_SUCCESS);
265 ASSERT(timebaseInfo.denom);
266 });
267
268 return fromRawSeconds((mach_absolute_time() * timebaseInfo.numer) / (1.0e9 * timebaseInfo.denom));
269#elif OS(FUCHSIA)
270 return fromRawSeconds(zx_clock_get_monotonic() / static_cast<double>(ZX_SEC(1)));
271#elif OS(LINUX) || OS(FREEBSD) || OS(OPENBSD) || OS(NETBSD)
272 struct timespec ts { };
273 clock_gettime(CLOCK_MONOTONIC, &ts);
274 return fromRawSeconds(static_cast<double>(ts.tv_sec) + ts.tv_nsec / 1.0e9);
275#else
276 static double lastTime = 0;
277 double currentTimeNow = currentTime();
278 if (currentTimeNow < lastTime)
279 return lastTime;
280 lastTime = currentTimeNow;
281 return fromRawSeconds(currentTimeNow);
282#endif
283}
284
285} // namespace WTF
286