CurrentTime.cpp revision cad810f21b803229eb11403f9209855525a25d57
1/*
2 * Copyright (C) 2006, 2010 Apple Inc. All rights reserved.
3 * Copyright (C) 2008 Google Inc. All rights reserved.
4 * Copyright (C) 2007-2009 Torch Mobile, Inc.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 *     * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 *     * Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following disclaimer
14 * in the documentation and/or other materials provided with the
15 * distribution.
16 *     * Neither the name of Google Inc. nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33#include "config.h"
34#include "CurrentTime.h"
35
36#if OS(WINDOWS)
37
38// Windows is first since we want to use hires timers, despite PLATFORM(CF)
39// being defined.
40// If defined, WIN32_LEAN_AND_MEAN disables timeBeginPeriod/timeEndPeriod.
41#undef WIN32_LEAN_AND_MEAN
42#include <windows.h>
43#include <math.h>
44#include <stdint.h>
45#include <time.h>
46
47#if USE(QUERY_PERFORMANCE_COUNTER)
48#if OS(WINCE)
49extern "C" time_t mktime(struct tm *t);
50#else
51#include <sys/timeb.h>
52#include <sys/types.h>
53#endif
54#endif
55
56#elif PLATFORM(GTK)
57#include <glib.h>
58#elif PLATFORM(WX)
59#include <wx/datetime.h>
60#elif PLATFORM(BREWMP)
61#include <AEEStdLib.h>
62#else
63#include <sys/time.h>
64#endif
65
66#if PLATFORM(CHROMIUM)
67#error Chromium uses a different timer implementation
68#endif
69
70namespace WTF {
71
72const double msPerSecond = 1000.0;
73
74#if OS(WINDOWS)
75
76#if USE(QUERY_PERFORMANCE_COUNTER)
77
78static LARGE_INTEGER qpcFrequency;
79static bool syncedTime;
80
81static double highResUpTime()
82{
83    // We use QPC, but only after sanity checking its result, due to bugs:
84    // http://support.microsoft.com/kb/274323
85    // http://support.microsoft.com/kb/895980
86    // 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)."
87
88    static LARGE_INTEGER qpcLast;
89    static DWORD tickCountLast;
90    static bool inited;
91
92    LARGE_INTEGER qpc;
93    QueryPerformanceCounter(&qpc);
94    DWORD tickCount = GetTickCount();
95
96    if (inited) {
97        __int64 qpcElapsed = ((qpc.QuadPart - qpcLast.QuadPart) * 1000) / qpcFrequency.QuadPart;
98        __int64 tickCountElapsed;
99        if (tickCount >= tickCountLast)
100            tickCountElapsed = (tickCount - tickCountLast);
101        else {
102#if COMPILER(MINGW)
103            __int64 tickCountLarge = tickCount + 0x100000000ULL;
104#else
105            __int64 tickCountLarge = tickCount + 0x100000000I64;
106#endif
107            tickCountElapsed = tickCountLarge - tickCountLast;
108        }
109
110        // force a re-sync if QueryPerformanceCounter differs from GetTickCount by more than 500ms.
111        // (500ms value is from http://support.microsoft.com/kb/274323)
112        __int64 diff = tickCountElapsed - qpcElapsed;
113        if (diff > 500 || diff < -500)
114            syncedTime = false;
115    } else
116        inited = true;
117
118    qpcLast = qpc;
119    tickCountLast = tickCount;
120
121    return (1000.0 * qpc.QuadPart) / static_cast<double>(qpcFrequency.QuadPart);
122}
123
124static double lowResUTCTime()
125{
126#if OS(WINCE)
127    SYSTEMTIME systemTime;
128    GetSystemTime(&systemTime);
129    struct tm tmtime;
130    tmtime.tm_year = systemTime.wYear - 1900;
131    tmtime.tm_mon = systemTime.wMonth - 1;
132    tmtime.tm_mday = systemTime.wDay;
133    tmtime.tm_wday = systemTime.wDayOfWeek;
134    tmtime.tm_hour = systemTime.wHour;
135    tmtime.tm_min = systemTime.wMinute;
136    tmtime.tm_sec = systemTime.wSecond;
137    time_t timet = mktime(&tmtime);
138    return timet * msPerSecond + systemTime.wMilliseconds;
139#else
140    struct _timeb timebuffer;
141    _ftime(&timebuffer);
142    return timebuffer.time * msPerSecond + timebuffer.millitm;
143#endif
144}
145
146static bool qpcAvailable()
147{
148    static bool available;
149    static bool checked;
150
151    if (checked)
152        return available;
153
154    available = QueryPerformanceFrequency(&qpcFrequency);
155    checked = true;
156    return available;
157}
158
159double currentTime()
160{
161    // Use a combination of ftime and QueryPerformanceCounter.
162    // ftime returns the information we want, but doesn't have sufficient resolution.
163    // QueryPerformanceCounter has high resolution, but is only usable to measure time intervals.
164    // To combine them, we call ftime and QueryPerformanceCounter initially. Later calls will use QueryPerformanceCounter
165    // by itself, adding the delta to the saved ftime.  We periodically re-sync to correct for drift.
166    static double syncLowResUTCTime;
167    static double syncHighResUpTime;
168    static double lastUTCTime;
169
170    double lowResTime = lowResUTCTime();
171
172    if (!qpcAvailable())
173        return lowResTime / 1000.0;
174
175    double highResTime = highResUpTime();
176
177    if (!syncedTime) {
178        timeBeginPeriod(1); // increase time resolution around low-res time getter
179        syncLowResUTCTime = lowResTime = lowResUTCTime();
180        timeEndPeriod(1); // restore time resolution
181        syncHighResUpTime = highResTime;
182        syncedTime = true;
183    }
184
185    double highResElapsed = highResTime - syncHighResUpTime;
186    double utc = syncLowResUTCTime + highResElapsed;
187
188    // force a clock re-sync if we've drifted
189    double lowResElapsed = lowResTime - syncLowResUTCTime;
190    const double maximumAllowedDriftMsec = 15.625 * 2.0; // 2x the typical low-res accuracy
191    if (fabs(highResElapsed - lowResElapsed) > maximumAllowedDriftMsec)
192        syncedTime = false;
193
194    // make sure time doesn't run backwards (only correct if difference is < 2 seconds, since DST or clock changes could occur)
195    const double backwardTimeLimit = 2000.0;
196    if (utc < lastUTCTime && (lastUTCTime - utc) < backwardTimeLimit)
197        return lastUTCTime / 1000.0;
198    lastUTCTime = utc;
199    return utc / 1000.0;
200}
201
202#else
203
204static double currentSystemTime()
205{
206    FILETIME ft;
207    GetCurrentFT(&ft);
208
209    // As per Windows documentation for FILETIME, copy the resulting FILETIME structure to a
210    // ULARGE_INTEGER structure using memcpy (using memcpy instead of direct assignment can
211    // prevent alignment faults on 64-bit Windows).
212
213    ULARGE_INTEGER t;
214    memcpy(&t, &ft, sizeof(t));
215
216    // Windows file times are in 100s of nanoseconds.
217    // To convert to seconds, we have to divide by 10,000,000, which is more quickly
218    // done by multiplying by 0.0000001.
219
220    // Between January 1, 1601 and January 1, 1970, there were 369 complete years,
221    // of which 89 were leap years (1700, 1800, and 1900 were not leap years).
222    // That is a total of 134774 days, which is 11644473600 seconds.
223
224    return t.QuadPart * 0.0000001 - 11644473600.0;
225}
226
227double currentTime()
228{
229    static bool init = false;
230    static double lastTime;
231    static DWORD lastTickCount;
232    if (!init) {
233        lastTime = currentSystemTime();
234        lastTickCount = GetTickCount();
235        init = true;
236        return lastTime;
237    }
238
239    DWORD tickCountNow = GetTickCount();
240    DWORD elapsed = tickCountNow - lastTickCount;
241    double timeNow = lastTime + (double)elapsed / 1000.;
242    if (elapsed >= 0x7FFFFFFF) {
243        lastTime = timeNow;
244        lastTickCount = tickCountNow;
245    }
246    return timeNow;
247}
248
249#endif // USE(QUERY_PERFORMANCE_COUNTER)
250
251#elif PLATFORM(GTK)
252
253// Note: GTK on Windows will pick up the PLATFORM(WIN) implementation above which provides
254// better accuracy compared with Windows implementation of g_get_current_time:
255// (http://www.google.com/codesearch/p?hl=en#HHnNRjks1t0/glib-2.5.2/glib/gmain.c&q=g_get_current_time).
256// Non-Windows GTK builds could use gettimeofday() directly but for the sake of consistency lets use GTK function.
257double currentTime()
258{
259    GTimeVal now;
260    g_get_current_time(&now);
261    return static_cast<double>(now.tv_sec) + static_cast<double>(now.tv_usec / 1000000.0);
262}
263
264#elif PLATFORM(WX)
265
266double currentTime()
267{
268    wxDateTime now = wxDateTime::UNow();
269    return (double)now.GetTicks() + (double)(now.GetMillisecond() / 1000.0);
270}
271
272#elif PLATFORM(BREWMP)
273
274// GETUTCSECONDS returns the number of seconds since 1980/01/06 00:00:00 UTC,
275// and GETTIMEMS returns the number of milliseconds that have elapsed since the last
276// occurrence of 00:00:00 local time.
277// We can combine GETUTCSECONDS and GETTIMEMS to calculate the number of milliseconds
278// since 1970/01/01 00:00:00 UTC.
279double currentTime()
280{
281    // diffSeconds is the number of seconds from 1970/01/01 to 1980/01/06
282    const unsigned diffSeconds = 315964800;
283    return static_cast<double>(diffSeconds + GETUTCSECONDS() + ((GETTIMEMS() % 1000) / msPerSecond));
284}
285
286#else
287
288double currentTime()
289{
290    struct timeval now;
291    gettimeofday(&now, 0);
292    return now.tv_sec + now.tv_usec / 1000000.0;
293}
294
295#endif
296
297} // namespace WTF
298