1 | /*
|
---|
2 | * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
|
---|
3 | * Copyright (C) 2008 Google Inc. All rights reserved.
|
---|
4 | *
|
---|
5 | * Redistribution and use in source and binary forms, with or without
|
---|
6 | * modification, are permitted provided that the following conditions are
|
---|
7 | * met:
|
---|
8 | *
|
---|
9 | * * Redistributions of source code must retain the above copyright
|
---|
10 | * notice, this list of conditions and the following disclaimer.
|
---|
11 | * * Redistributions in binary form must reproduce the above
|
---|
12 | * copyright notice, this list of conditions and the following disclaimer
|
---|
13 | * in the documentation and/or other materials provided with the
|
---|
14 | * distribution.
|
---|
15 | * * Neither the name of Google Inc. nor the names of its
|
---|
16 | * contributors may be used to endorse or promote products derived from
|
---|
17 | * this software without specific prior written permission.
|
---|
18 | *
|
---|
19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
---|
20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
---|
21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
---|
22 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
---|
23 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
---|
24 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
---|
25 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
---|
26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
---|
27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
---|
28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
---|
29 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
---|
30 | */
|
---|
31 |
|
---|
32 | #include "config.h"
|
---|
33 | #include "CurrentTime.h"
|
---|
34 |
|
---|
35 | #if PLATFORM(WIN_OS)
|
---|
36 | // Windows is first since we want to use hires timers, despite PLATFORM(CF)
|
---|
37 | // being defined.
|
---|
38 | // If defined, WIN32_LEAN_AND_MEAN disables timeBeginPeriod/timeEndPeriod.
|
---|
39 | #undef WIN32_LEAN_AND_MEAN
|
---|
40 | #include <windows.h>
|
---|
41 | #include <math.h>
|
---|
42 | #include <stdint.h>
|
---|
43 | #include <sys/timeb.h>
|
---|
44 | #include <sys/types.h>
|
---|
45 | #include <time.h>
|
---|
46 | #elif PLATFORM(CF)
|
---|
47 | #include <CoreFoundation/CFDate.h>
|
---|
48 | #elif PLATFORM(GTK)
|
---|
49 | #include <glib.h>
|
---|
50 | #elif PLATFORM(WX)
|
---|
51 | #include <wx/datetime.h>
|
---|
52 | #else // Posix systems relying on the gettimeofday()
|
---|
53 | #include <sys/time.h>
|
---|
54 | #endif
|
---|
55 |
|
---|
56 | namespace WTF {
|
---|
57 |
|
---|
58 | const double msPerSecond = 1000.0;
|
---|
59 |
|
---|
60 | #if PLATFORM(WIN_OS)
|
---|
61 |
|
---|
62 | static LARGE_INTEGER qpcFrequency;
|
---|
63 | static bool syncedTime;
|
---|
64 |
|
---|
65 | static double highResUpTime()
|
---|
66 | {
|
---|
67 | // We use QPC, but only after sanity checking its result, due to bugs:
|
---|
68 | // http://support.microsoft.com/kb/274323
|
---|
69 | // http://support.microsoft.com/kb/895980
|
---|
70 | // 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)."
|
---|
71 |
|
---|
72 | static LARGE_INTEGER qpcLast;
|
---|
73 | static DWORD tickCountLast;
|
---|
74 | static bool inited;
|
---|
75 |
|
---|
76 | LARGE_INTEGER qpc;
|
---|
77 | QueryPerformanceCounter(&qpc);
|
---|
78 | DWORD tickCount = GetTickCount();
|
---|
79 |
|
---|
80 | if (inited) {
|
---|
81 | __int64 qpcElapsed = ((qpc.QuadPart - qpcLast.QuadPart) * 1000) / qpcFrequency.QuadPart;
|
---|
82 | __int64 tickCountElapsed;
|
---|
83 | if (tickCount >= tickCountLast)
|
---|
84 | tickCountElapsed = (tickCount - tickCountLast);
|
---|
85 | else {
|
---|
86 | #if COMPILER(MINGW)
|
---|
87 | __int64 tickCountLarge = tickCount + 0x100000000ULL;
|
---|
88 | #else
|
---|
89 | __int64 tickCountLarge = tickCount + 0x100000000I64;
|
---|
90 | #endif
|
---|
91 | tickCountElapsed = tickCountLarge - tickCountLast;
|
---|
92 | }
|
---|
93 |
|
---|
94 | // force a re-sync if QueryPerformanceCounter differs from GetTickCount by more than 500ms.
|
---|
95 | // (500ms value is from http://support.microsoft.com/kb/274323)
|
---|
96 | __int64 diff = tickCountElapsed - qpcElapsed;
|
---|
97 | if (diff > 500 || diff < -500)
|
---|
98 | syncedTime = false;
|
---|
99 | } else
|
---|
100 | inited = true;
|
---|
101 |
|
---|
102 | qpcLast = qpc;
|
---|
103 | tickCountLast = tickCount;
|
---|
104 |
|
---|
105 | return (1000.0 * qpc.QuadPart) / static_cast<double>(qpcFrequency.QuadPart);
|
---|
106 | }
|
---|
107 |
|
---|
108 | static double lowResUTCTime()
|
---|
109 | {
|
---|
110 | #if PLATFORM(WIN_CE)
|
---|
111 | SYSTEMTIME systemTime;
|
---|
112 | GetSystemTime(&systemTime);
|
---|
113 | struct tm tmtime;
|
---|
114 | tmtime.tm_year = systemTime.wYear - 1900;
|
---|
115 | tmtime.tm_mon = systemTime.wMonth - 1;
|
---|
116 | tmtime.tm_mday = systemTime.wDay;
|
---|
117 | tmtime.tm_wday = systemTime.wDayOfWeek;
|
---|
118 | tmtime.tm_hour = systemTime.wHour;
|
---|
119 | tmtime.tm_min = systemTime.wMinute;
|
---|
120 | tmtime.tm_sec = systemTime.wSecond;
|
---|
121 | time_t timet = mktime(&tmtime);
|
---|
122 | return timet * msPerSecond + systemTime.wMilliseconds;
|
---|
123 | #else // PLATFORM(WIN_CE)
|
---|
124 | struct _timeb timebuffer;
|
---|
125 | _ftime(&timebuffer);
|
---|
126 | return timebuffer.time * msPerSecond + timebuffer.millitm;
|
---|
127 | #endif // PLATFORM(WIN_CE)
|
---|
128 | }
|
---|
129 |
|
---|
130 | static bool qpcAvailable()
|
---|
131 | {
|
---|
132 | static bool available;
|
---|
133 | static bool checked;
|
---|
134 |
|
---|
135 | if (checked)
|
---|
136 | return available;
|
---|
137 |
|
---|
138 | available = QueryPerformanceFrequency(&qpcFrequency);
|
---|
139 | checked = true;
|
---|
140 | return available;
|
---|
141 | }
|
---|
142 |
|
---|
143 | double currentTime()
|
---|
144 | {
|
---|
145 | // Use a combination of ftime and QueryPerformanceCounter.
|
---|
146 | // ftime returns the information we want, but doesn't have sufficient resolution.
|
---|
147 | // QueryPerformanceCounter has high resolution, but is only usable to measure time intervals.
|
---|
148 | // To combine them, we call ftime and QueryPerformanceCounter initially. Later calls will use QueryPerformanceCounter
|
---|
149 | // by itself, adding the delta to the saved ftime. We periodically re-sync to correct for drift.
|
---|
150 | static bool started;
|
---|
151 | static double syncLowResUTCTime;
|
---|
152 | static double syncHighResUpTime;
|
---|
153 | static double lastUTCTime;
|
---|
154 |
|
---|
155 | double lowResTime = lowResUTCTime();
|
---|
156 |
|
---|
157 | if (!qpcAvailable())
|
---|
158 | return lowResTime / 1000.0;
|
---|
159 |
|
---|
160 | double highResTime = highResUpTime();
|
---|
161 |
|
---|
162 | if (!syncedTime) {
|
---|
163 | timeBeginPeriod(1); // increase time resolution around low-res time getter
|
---|
164 | syncLowResUTCTime = lowResTime = lowResUTCTime();
|
---|
165 | timeEndPeriod(1); // restore time resolution
|
---|
166 | syncHighResUpTime = highResTime;
|
---|
167 | syncedTime = true;
|
---|
168 | }
|
---|
169 |
|
---|
170 | double highResElapsed = highResTime - syncHighResUpTime;
|
---|
171 | double utc = syncLowResUTCTime + highResElapsed;
|
---|
172 |
|
---|
173 | // force a clock re-sync if we've drifted
|
---|
174 | double lowResElapsed = lowResTime - syncLowResUTCTime;
|
---|
175 | const double maximumAllowedDriftMsec = 15.625 * 2.0; // 2x the typical low-res accuracy
|
---|
176 | if (fabs(highResElapsed - lowResElapsed) > maximumAllowedDriftMsec)
|
---|
177 | syncedTime = false;
|
---|
178 |
|
---|
179 | // make sure time doesn't run backwards (only correct if difference is < 2 seconds, since DST or clock changes could occur)
|
---|
180 | const double backwardTimeLimit = 2000.0;
|
---|
181 | if (utc < lastUTCTime && (lastUTCTime - utc) < backwardTimeLimit)
|
---|
182 | return lastUTCTime / 1000.0;
|
---|
183 | lastUTCTime = utc;
|
---|
184 | return utc / 1000.0;
|
---|
185 | }
|
---|
186 |
|
---|
187 | #elif PLATFORM(CF)
|
---|
188 |
|
---|
189 | double currentTime()
|
---|
190 | {
|
---|
191 | return CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970;
|
---|
192 | }
|
---|
193 |
|
---|
194 | #elif PLATFORM(GTK)
|
---|
195 |
|
---|
196 | // Note: GTK on Windows will pick up the PLATFORM(WIN) implementation above which provides
|
---|
197 | // better accuracy compared with Windows implementation of g_get_current_time:
|
---|
198 | // (http://www.google.com/codesearch/p?hl=en#HHnNRjks1t0/glib-2.5.2/glib/gmain.c&q=g_get_current_time).
|
---|
199 | // Non-Windows GTK builds could use gettimeofday() directly but for the sake of consistency lets use GTK function.
|
---|
200 | double currentTime()
|
---|
201 | {
|
---|
202 | GTimeVal now;
|
---|
203 | g_get_current_time(&now);
|
---|
204 | return static_cast<double>(now.tv_sec) + static_cast<double>(now.tv_usec / 1000000.0);
|
---|
205 | }
|
---|
206 |
|
---|
207 | #elif PLATFORM(WX)
|
---|
208 |
|
---|
209 | double currentTime()
|
---|
210 | {
|
---|
211 | wxDateTime now = wxDateTime::UNow();
|
---|
212 | return (double)now.GetTicks() + (double)(now.GetMillisecond() / 1000.0);
|
---|
213 | }
|
---|
214 |
|
---|
215 | #else // Other Posix systems rely on the gettimeofday().
|
---|
216 |
|
---|
217 | double currentTime()
|
---|
218 | {
|
---|
219 | struct timeval now;
|
---|
220 | struct timezone zone;
|
---|
221 |
|
---|
222 | gettimeofday(&now, &zone);
|
---|
223 | return static_cast<double>(now.tv_sec) + (double)(now.tv_usec / 1000000.0);
|
---|
224 | }
|
---|
225 |
|
---|
226 | #endif
|
---|
227 |
|
---|
228 | } // namespace WTF
|
---|