1/*
2 * Copyright (C) 2006, 2008, 2009, 2010 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 <cwzwarich@uwaterloo.ca>
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 "CurrentTime.h"
36
37#if OS(DARWIN)
38#include <mach/mach.h>
39#include <mach/mach_time.h>
40#include <mutex>
41#include <sys/time.h>
42#elif OS(WINDOWS)
43
44// Windows is first since we want to use hires timers, despite USE(CF)
45// being defined.
46// If defined, WIN32_LEAN_AND_MEAN disables timeBeginPeriod/timeEndPeriod.
47#undef WIN32_LEAN_AND_MEAN
48#include <windows.h>
49#include <math.h>
50#include <stdint.h>
51#include <time.h>
52
53#elif PLATFORM(EFL)
54#include <Ecore.h>
55#else
56#include <sys/time.h>
57#endif
58
59#if USE(GLIB) && !PLATFORM(EFL)
60#include <glib.h>
61#endif
62
63namespace WTF {
64
65#if OS(WINDOWS)
66
67// Number of 100 nanosecond between January 1, 1601 and January 1, 1970.
68static const ULONGLONG epochBias = 116444736000000000ULL;
69static const double hundredsOfNanosecondsPerMillisecond = 10000;
70
71static double lowResUTCTime()
72{
73    FILETIME fileTime;
74
75#if OS(WINCE)
76    GetCurrentFT(&fileTime);
77#else
78    GetSystemTimeAsFileTime(&fileTime);
79#endif
80
81    // As per Windows documentation for FILETIME, copy the resulting FILETIME structure to a
82    // ULARGE_INTEGER structure using memcpy (using memcpy instead of direct assignment can
83    // prevent alignment faults on 64-bit Windows).
84
85    ULARGE_INTEGER dateTime;
86    memcpy(&dateTime, &fileTime, sizeof(dateTime));
87
88    // Windows file times are in 100s of nanoseconds.
89    return (dateTime.QuadPart - epochBias) / hundredsOfNanosecondsPerMillisecond;
90}
91
92#if USE(QUERY_PERFORMANCE_COUNTER)
93
94static LARGE_INTEGER qpcFrequency;
95static bool syncedTime;
96
97static double highResUpTime()
98{
99    // We use QPC, but only after sanity checking its result, due to bugs:
100    // http://support.microsoft.com/kb/274323
101    // http://support.microsoft.com/kb/895980
102    // 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)."
103
104    static LARGE_INTEGER qpcLast;
105    static DWORD tickCountLast;
106    static bool inited;
107
108    LARGE_INTEGER qpc;
109    QueryPerformanceCounter(&qpc);
110#if defined(_M_IX86) || defined(__i386__)
111    DWORD tickCount = GetTickCount();
112#else
113    ULONGLONG tickCount = GetTickCount64();
114#endif
115
116    if (inited) {
117        __int64 qpcElapsed = ((qpc.QuadPart - qpcLast.QuadPart) * 1000) / qpcFrequency.QuadPart;
118        __int64 tickCountElapsed;
119        if (tickCount >= tickCountLast)
120            tickCountElapsed = (tickCount - tickCountLast);
121        else {
122#if COMPILER(MINGW)
123            __int64 tickCountLarge = tickCount + 0x100000000ULL;
124#else
125            __int64 tickCountLarge = tickCount + 0x100000000I64;
126#endif
127            tickCountElapsed = tickCountLarge - tickCountLast;
128        }
129
130        // force a re-sync if QueryPerformanceCounter differs from GetTickCount by more than 500ms.
131        // (500ms value is from http://support.microsoft.com/kb/274323)
132        __int64 diff = tickCountElapsed - qpcElapsed;
133        if (diff > 500 || diff < -500)
134            syncedTime = false;
135    } else
136        inited = true;
137
138    qpcLast = qpc;
139    tickCountLast = tickCount;
140
141    return (1000.0 * qpc.QuadPart) / static_cast<double>(qpcFrequency.QuadPart);
142}
143
144static bool qpcAvailable()
145{
146    static bool available;
147    static bool checked;
148
149    if (checked)
150        return available;
151
152    available = QueryPerformanceFrequency(&qpcFrequency);
153    checked = true;
154    return available;
155}
156
157double currentTime()
158{
159    // Use a combination of ftime and QueryPerformanceCounter.
160    // ftime returns the information we want, but doesn't have sufficient resolution.
161    // QueryPerformanceCounter has high resolution, but is only usable to measure time intervals.
162    // To combine them, we call ftime and QueryPerformanceCounter initially. Later calls will use QueryPerformanceCounter
163    // by itself, adding the delta to the saved ftime.  We periodically re-sync to correct for drift.
164    static double syncLowResUTCTime;
165    static double syncHighResUpTime;
166    static double lastUTCTime;
167
168    double lowResTime = lowResUTCTime();
169
170    if (!qpcAvailable())
171        return lowResTime / 1000.0;
172
173    double highResTime = highResUpTime();
174
175    if (!syncedTime) {
176        timeBeginPeriod(1); // increase time resolution around low-res time getter
177        syncLowResUTCTime = lowResTime = lowResUTCTime();
178        timeEndPeriod(1); // restore time resolution
179        syncHighResUpTime = highResTime;
180        syncedTime = true;
181    }
182
183    double highResElapsed = highResTime - syncHighResUpTime;
184    double utc = syncLowResUTCTime + highResElapsed;
185
186    // force a clock re-sync if we've drifted
187    double lowResElapsed = lowResTime - syncLowResUTCTime;
188    const double maximumAllowedDriftMsec = 15.625 * 2.0; // 2x the typical low-res accuracy
189    if (fabs(highResElapsed - lowResElapsed) > maximumAllowedDriftMsec)
190        syncedTime = false;
191
192    // make sure time doesn't run backwards (only correct if difference is < 2 seconds, since DST or clock changes could occur)
193    const double backwardTimeLimit = 2000.0;
194    if (utc < lastUTCTime && (lastUTCTime - utc) < backwardTimeLimit)
195        return lastUTCTime / 1000.0;
196    lastUTCTime = utc;
197    return utc / 1000.0;
198}
199
200#else
201
202double currentTime()
203{
204    static bool init = false;
205    static double lastTime;
206    static DWORD lastTickCount;
207    if (!init) {
208        lastTime = lowResUTCTime();
209        lastTickCount = GetTickCount();
210        init = true;
211        return lastTime;
212    }
213
214    DWORD tickCountNow = GetTickCount();
215    DWORD elapsed = tickCountNow - lastTickCount;
216    double timeNow = lastTime + (double)elapsed / 1000.;
217    if (elapsed >= 0x7FFFFFFF) {
218        lastTime = timeNow;
219        lastTickCount = tickCountNow;
220    }
221    return timeNow;
222}
223
224#endif // USE(QUERY_PERFORMANCE_COUNTER)
225
226#elif USE(GLIB) && !PLATFORM(EFL)
227
228// Note: GTK on Windows will pick up the PLATFORM(WIN) implementation above which provides
229// better accuracy compared with Windows implementation of g_get_current_time:
230// (http://www.google.com/codesearch/p?hl=en#HHnNRjks1t0/glib-2.5.2/glib/gmain.c&q=g_get_current_time).
231// Non-Windows GTK builds could use gettimeofday() directly but for the sake of consistency lets use GTK function.
232double currentTime()
233{
234    GTimeVal now;
235    g_get_current_time(&now);
236    return static_cast<double>(now.tv_sec) + static_cast<double>(now.tv_usec / 1000000.0);
237}
238
239#elif PLATFORM(EFL)
240
241double currentTime()
242{
243    return ecore_time_unix_get();
244}
245
246#else
247
248double currentTime()
249{
250    struct timeval now;
251    gettimeofday(&now, 0);
252    return now.tv_sec + now.tv_usec / 1000000.0;
253}
254
255#endif
256
257#if PLATFORM(EFL)
258
259double monotonicallyIncreasingTime()
260{
261    return ecore_time_get();
262}
263
264#elif USE(GLIB)
265
266double monotonicallyIncreasingTime()
267{
268    return static_cast<double>(g_get_monotonic_time() / 1000000.0);
269}
270
271#elif OS(DARWIN)
272
273double monotonicallyIncreasingTime()
274{
275    // Based on listing #2 from Apple QA 1398, but modified to be thread-safe.
276    static mach_timebase_info_data_t timebaseInfo;
277    static std::once_flag initializeTimerOnceFlag;
278    std::call_once(initializeTimerOnceFlag, [] {
279        kern_return_t kr = mach_timebase_info(&timebaseInfo);
280        ASSERT_UNUSED(kr, kr == KERN_SUCCESS);
281        ASSERT(timebaseInfo.denom);
282    });
283
284    return (mach_absolute_time() * timebaseInfo.numer) / (1.0e9 * timebaseInfo.denom);
285}
286
287#else
288
289double monotonicallyIncreasingTime()
290{
291    static double lastTime = 0;
292    double currentTimeNow = currentTime();
293    if (currentTimeNow < lastTime)
294        return lastTime;
295    lastTime = currentTimeNow;
296    return currentTimeNow;
297}
298
299#endif
300
301double currentCPUTime()
302{
303#if OS(DARWIN)
304    mach_msg_type_number_t infoCount = THREAD_BASIC_INFO_COUNT;
305    thread_basic_info_data_t info;
306
307    // Get thread information
308    mach_port_t threadPort = mach_thread_self();
309    thread_info(threadPort, THREAD_BASIC_INFO, reinterpret_cast<thread_info_t>(&info), &infoCount);
310    mach_port_deallocate(mach_task_self(), threadPort);
311
312    double time = info.user_time.seconds + info.user_time.microseconds / 1000000.;
313    time += info.system_time.seconds + info.system_time.microseconds / 1000000.;
314
315    return time;
316#elif OS(WINDOWS)
317    union {
318        FILETIME fileTime;
319        unsigned long long fileTimeAsLong;
320    } userTime, kernelTime;
321
322    // GetThreadTimes won't accept null arguments so we pass these even though
323    // they're not used.
324    FILETIME creationTime, exitTime;
325
326    GetThreadTimes(GetCurrentThread(), &creationTime, &exitTime, &kernelTime.fileTime, &userTime.fileTime);
327
328    return userTime.fileTimeAsLong / 10000000. + kernelTime.fileTimeAsLong / 10000000.;
329#else
330    // FIXME: We should return the time the current thread has spent executing.
331
332    // use a relative time from first call in order to avoid an overflow
333    static double firstTime = currentTime();
334    return currentTime() - firstTime;
335#endif
336}
337
338double currentCPUTimeMS()
339{
340    return currentCPUTime() * 1000;
341}
342
343} // namespace WTF
344