[email protected] | 190f13f7 | 2012-05-01 20:56:43 | [diff] [blame] | 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
license.bot | bf09a50 | 2008-08-24 00:55:55 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 4 | |
[email protected] | de592d3 | 2008-09-26 03:00:00 | [diff] [blame] | 5 | |
| 6 | // Windows Timer Primer |
| 7 | // |
| 8 | // A good article: http://www.ddj.com/windows/184416651 |
| 9 | // A good mozilla bug: http://bugzilla.mozilla.org/show_bug.cgi?id=363258 |
| 10 | // |
| 11 | // The default windows timer, GetSystemTimeAsFileTime is not very precise. |
| 12 | // It is only good to ~15.5ms. |
| 13 | // |
| 14 | // QueryPerformanceCounter is the logical choice for a high-precision timer. |
| 15 | // However, it is known to be buggy on some hardware. Specifically, it can |
| 16 | // sometimes "jump". On laptops, QPC can also be very expensive to call. |
| 17 | // It's 3-4x slower than timeGetTime() on desktops, but can be 10x slower |
| 18 | // on laptops. A unittest exists which will show the relative cost of various |
| 19 | // timers on any system. |
| 20 | // |
| 21 | // The next logical choice is timeGetTime(). timeGetTime has a precision of |
| 22 | // 1ms, but only if you call APIs (timeBeginPeriod()) which affect all other |
| 23 | // applications on the system. By default, precision is only 15.5ms. |
| 24 | // Unfortunately, we don't want to call timeBeginPeriod because we don't |
| 25 | // want to affect other applications. Further, on mobile platforms, use of |
[email protected] | 52a261f | 2009-03-03 15:01:12 | [diff] [blame] | 26 | // faster multimedia timers can hurt battery life. See the intel |
| 27 | // article about this here: |
[email protected] | de592d3 | 2008-09-26 03:00:00 | [diff] [blame] | 28 | // http://softwarecommunity.intel.com/articles/eng/1086.htm |
| 29 | // |
| 30 | // To work around all this, we're going to generally use timeGetTime(). We |
| 31 | // will only increase the system-wide timer if we're not running on battery |
| 32 | // power. Using timeBeginPeriod(1) is a requirement in order to make our |
| 33 | // message loop waits have the same resolution that our time measurements |
| 34 | // do. Otherwise, WaitForSingleObject(..., 1) will no less than 15ms when |
| 35 | // there is nothing else to waken the Wait. |
| 36 | |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 37 | #include "base/time.h" |
| 38 | |
| 39 | #pragma comment(lib, "winmm.lib") |
| 40 | #include <windows.h> |
| 41 | #include <mmsystem.h> |
[email protected] | c5324208 | 2008-09-09 08:58:51 | [diff] [blame] | 42 | |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 43 | #include "base/basictypes.h" |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 44 | #include "base/logging.h" |
[email protected] | de592d3 | 2008-09-26 03:00:00 | [diff] [blame] | 45 | #include "base/cpu.h" |
[email protected] | 3b63f8f4 | 2011-03-28 01:54:15 | [diff] [blame] | 46 | #include "base/memory/singleton.h" |
[email protected] | 20305ec | 2011-01-21 04:55:52 | [diff] [blame] | 47 | #include "base/synchronization/lock.h" |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 48 | |
[email protected] | e1acf6f | 2008-10-27 20:43:33 | [diff] [blame] | 49 | using base::Time; |
| 50 | using base::TimeDelta; |
| 51 | using base::TimeTicks; |
| 52 | |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 53 | namespace { |
| 54 | |
| 55 | // From MSDN, FILETIME "Contains a 64-bit value representing the number of |
| 56 | // 100-nanosecond intervals since January 1, 1601 (UTC)." |
| 57 | int64 FileTimeToMicroseconds(const FILETIME& ft) { |
| 58 | // Need to bit_cast to fix alignment, then divide by 10 to convert |
| 59 | // 100-nanoseconds to milliseconds. This only works on little-endian |
| 60 | // machines. |
| 61 | return bit_cast<int64, FILETIME>(ft) / 10; |
| 62 | } |
| 63 | |
| 64 | void MicrosecondsToFileTime(int64 us, FILETIME* ft) { |
[email protected] | 7e612db | 2011-07-06 20:56:57 | [diff] [blame] | 65 | DCHECK_GE(us, 0LL) << "Time is less than 0, negative values are not " |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 66 | "representable in FILETIME"; |
| 67 | |
| 68 | // Multiply by 10 to convert milliseconds to 100-nanoseconds. Bit_cast will |
| 69 | // handle alignment problems. This only works on little-endian machines. |
| 70 | *ft = bit_cast<FILETIME, int64>(us * 10); |
| 71 | } |
| 72 | |
[email protected] | 7903e02 | 2008-09-11 14:06:48 | [diff] [blame] | 73 | int64 CurrentWallclockMicroseconds() { |
| 74 | FILETIME ft; |
| 75 | ::GetSystemTimeAsFileTime(&ft); |
| 76 | return FileTimeToMicroseconds(ft); |
| 77 | } |
| 78 | |
| 79 | // Time between resampling the un-granular clock for this API. 60 seconds. |
| 80 | const int kMaxMillisecondsToAvoidDrift = 60 * Time::kMillisecondsPerSecond; |
| 81 | |
| 82 | int64 initial_time = 0; |
| 83 | TimeTicks initial_ticks; |
| 84 | |
| 85 | void InitializeClock() { |
| 86 | initial_ticks = TimeTicks::Now(); |
| 87 | initial_time = CurrentWallclockMicroseconds(); |
| 88 | } |
| 89 | |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 90 | } // namespace |
| 91 | |
| 92 | // Time ----------------------------------------------------------------------- |
| 93 | |
| 94 | // The internal representation of Time uses FILETIME, whose epoch is 1601-01-01 |
| 95 | // 00:00:00 UTC. ((1970-1601)*365+89)*24*60*60*1000*1000, where 89 is the |
| 96 | // number of leap year days between 1601 and 1970: (1970-1601)/4 excluding |
| 97 | // 1700, 1800, and 1900. |
| 98 | // static |
| 99 | const int64 Time::kTimeTToMicrosecondsOffset = GG_INT64_C(11644473600000000); |
| 100 | |
[email protected] | 57f030a | 2010-06-29 04:58:15 | [diff] [blame] | 101 | bool Time::high_resolution_timer_enabled_ = false; |
[email protected] | 14255a99 | 2011-05-15 19:20:49 | [diff] [blame] | 102 | int Time::high_resolution_timer_activated_ = 0; |
[email protected] | 57f030a | 2010-06-29 04:58:15 | [diff] [blame] | 103 | |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 104 | // static |
[email protected] | 7903e02 | 2008-09-11 14:06:48 | [diff] [blame] | 105 | Time Time::Now() { |
| 106 | if (initial_time == 0) |
| 107 | InitializeClock(); |
| 108 | |
| 109 | // We implement time using the high-resolution timers so that we can get |
| 110 | // timeouts which are smaller than 10-15ms. If we just used |
| 111 | // CurrentWallclockMicroseconds(), we'd have the less-granular timer. |
| 112 | // |
| 113 | // To make this work, we initialize the clock (initial_time) and the |
| 114 | // counter (initial_ctr). To compute the initial time, we can check |
| 115 | // the number of ticks that have elapsed, and compute the delta. |
| 116 | // |
| 117 | // To avoid any drift, we periodically resync the counters to the system |
| 118 | // clock. |
[email protected] | 2fdc86a | 2010-01-26 23:08:02 | [diff] [blame] | 119 | while (true) { |
[email protected] | 7903e02 | 2008-09-11 14:06:48 | [diff] [blame] | 120 | TimeTicks ticks = TimeTicks::Now(); |
| 121 | |
| 122 | // Calculate the time elapsed since we started our timer |
| 123 | TimeDelta elapsed = ticks - initial_ticks; |
| 124 | |
| 125 | // Check if enough time has elapsed that we need to resync the clock. |
| 126 | if (elapsed.InMilliseconds() > kMaxMillisecondsToAvoidDrift) { |
| 127 | InitializeClock(); |
| 128 | continue; |
| 129 | } |
| 130 | |
[email protected] | b842d4c6 | 2009-09-14 18:49:03 | [diff] [blame] | 131 | return Time(elapsed + Time(initial_time)); |
[email protected] | 7903e02 | 2008-09-11 14:06:48 | [diff] [blame] | 132 | } |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 133 | } |
| 134 | |
| 135 | // static |
[email protected] | 85786f9 | 2009-04-18 00:42:48 | [diff] [blame] | 136 | Time Time::NowFromSystemTime() { |
| 137 | // Force resync. |
| 138 | InitializeClock(); |
| 139 | return Time(initial_time); |
| 140 | } |
| 141 | |
| 142 | // static |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 143 | Time Time::FromFileTime(FILETIME ft) { |
[email protected] | a9e5f044 | 2012-09-08 17:50:07 | [diff] [blame] | 144 | if (bit_cast<int64, FILETIME>(ft) == 0) |
| 145 | return Time(); |
| 146 | if (ft.dwHighDateTime == std::numeric_limits<DWORD>::max() && |
| 147 | ft.dwLowDateTime == std::numeric_limits<DWORD>::max()) |
| 148 | return Max(); |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 149 | return Time(FileTimeToMicroseconds(ft)); |
| 150 | } |
| 151 | |
| 152 | FILETIME Time::ToFileTime() const { |
[email protected] | a9e5f044 | 2012-09-08 17:50:07 | [diff] [blame] | 153 | if (is_null()) |
| 154 | return bit_cast<FILETIME, int64>(0); |
| 155 | if (is_max()) { |
| 156 | FILETIME result; |
| 157 | result.dwHighDateTime = std::numeric_limits<DWORD>::max(); |
| 158 | result.dwLowDateTime = std::numeric_limits<DWORD>::max(); |
| 159 | return result; |
| 160 | } |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 161 | FILETIME utc_ft; |
| 162 | MicrosecondsToFileTime(us_, &utc_ft); |
| 163 | return utc_ft; |
| 164 | } |
| 165 | |
| 166 | // static |
[email protected] | 57f030a | 2010-06-29 04:58:15 | [diff] [blame] | 167 | void Time::EnableHighResolutionTimer(bool enable) { |
| 168 | // Test for single-threaded access. |
| 169 | static PlatformThreadId my_thread = PlatformThread::CurrentId(); |
| 170 | DCHECK(PlatformThread::CurrentId() == my_thread); |
[email protected] | bc732c2 | 2009-05-27 08:12:55 | [diff] [blame] | 171 | |
[email protected] | 57f030a | 2010-06-29 04:58:15 | [diff] [blame] | 172 | if (high_resolution_timer_enabled_ == enable) |
| 173 | return; |
| 174 | |
| 175 | high_resolution_timer_enabled_ = enable; |
| 176 | } |
| 177 | |
| 178 | // static |
[email protected] | 14255a99 | 2011-05-15 19:20:49 | [diff] [blame] | 179 | bool Time::ActivateHighResolutionTimer(bool activating) { |
| 180 | if (!high_resolution_timer_enabled_ && activating) |
[email protected] | 57f030a | 2010-06-29 04:58:15 | [diff] [blame] | 181 | return false; |
| 182 | |
| 183 | // Using anything other than 1ms makes timers granular |
| 184 | // to that interval. |
| 185 | const int kMinTimerIntervalMs = 1; |
[email protected] | 097ae5a | 2009-11-26 19:48:34 | [diff] [blame] | 186 | MMRESULT result; |
[email protected] | 14255a99 | 2011-05-15 19:20:49 | [diff] [blame] | 187 | if (activating) { |
[email protected] | 57f030a | 2010-06-29 04:58:15 | [diff] [blame] | 188 | result = timeBeginPeriod(kMinTimerIntervalMs); |
[email protected] | 14255a99 | 2011-05-15 19:20:49 | [diff] [blame] | 189 | high_resolution_timer_activated_++; |
| 190 | } else { |
[email protected] | 57f030a | 2010-06-29 04:58:15 | [diff] [blame] | 191 | result = timeEndPeriod(kMinTimerIntervalMs); |
[email protected] | 14255a99 | 2011-05-15 19:20:49 | [diff] [blame] | 192 | high_resolution_timer_activated_--; |
| 193 | } |
[email protected] | 57f030a | 2010-06-29 04:58:15 | [diff] [blame] | 194 | return result == TIMERR_NOERROR; |
[email protected] | bc732c2 | 2009-05-27 08:12:55 | [diff] [blame] | 195 | } |
| 196 | |
| 197 | // static |
[email protected] | 14255a99 | 2011-05-15 19:20:49 | [diff] [blame] | 198 | bool Time::IsHighResolutionTimerInUse() { |
| 199 | // Note: we should track the high_resolution_timer_activated_ value |
| 200 | // under a lock if we want it to be accurate in a system with multiple |
| 201 | // message loops. We don't do that - because we don't want to take the |
| 202 | // expense of a lock for this. We *only* track this value so that unit |
| 203 | // tests can see if the high resolution timer is on or off. |
| 204 | return high_resolution_timer_enabled_ && |
| 205 | high_resolution_timer_activated_ > 0; |
| 206 | } |
| 207 | |
| 208 | // static |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 209 | Time Time::FromExploded(bool is_local, const Exploded& exploded) { |
| 210 | // Create the system struct representing our exploded time. It will either be |
| 211 | // in local time or UTC. |
| 212 | SYSTEMTIME st; |
| 213 | st.wYear = exploded.year; |
| 214 | st.wMonth = exploded.month; |
| 215 | st.wDayOfWeek = exploded.day_of_week; |
| 216 | st.wDay = exploded.day_of_month; |
| 217 | st.wHour = exploded.hour; |
| 218 | st.wMinute = exploded.minute; |
| 219 | st.wSecond = exploded.second; |
| 220 | st.wMilliseconds = exploded.millisecond; |
| 221 | |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 222 | FILETIME ft; |
[email protected] | 7e612db | 2011-07-06 20:56:57 | [diff] [blame] | 223 | bool success = true; |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 224 | // Ensure that it's in UTC. |
| 225 | if (is_local) { |
[email protected] | 7e612db | 2011-07-06 20:56:57 | [diff] [blame] | 226 | SYSTEMTIME utc_st; |
| 227 | success = TzSpecificLocalTimeToSystemTime(NULL, &st, &utc_st) && |
| 228 | SystemTimeToFileTime(&utc_st, &ft); |
| 229 | } else { |
| 230 | success = !!SystemTimeToFileTime(&st, &ft); |
| 231 | } |
| 232 | |
| 233 | if (!success) { |
| 234 | NOTREACHED() << "Unable to convert time"; |
| 235 | return Time(0); |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 236 | } |
| 237 | return Time(FileTimeToMicroseconds(ft)); |
| 238 | } |
| 239 | |
| 240 | void Time::Explode(bool is_local, Exploded* exploded) const { |
[email protected] | 7e612db | 2011-07-06 20:56:57 | [diff] [blame] | 241 | if (us_ < 0LL) { |
| 242 | // We are not able to convert it to FILETIME. |
| 243 | ZeroMemory(exploded, sizeof(*exploded)); |
| 244 | return; |
| 245 | } |
| 246 | |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 247 | // FILETIME in UTC. |
| 248 | FILETIME utc_ft; |
| 249 | MicrosecondsToFileTime(us_, &utc_ft); |
| 250 | |
| 251 | // FILETIME in local time if necessary. |
[email protected] | 7e612db | 2011-07-06 20:56:57 | [diff] [blame] | 252 | bool success = true; |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 253 | // FILETIME in SYSTEMTIME (exploded). |
| 254 | SYSTEMTIME st; |
[email protected] | 7e612db | 2011-07-06 20:56:57 | [diff] [blame] | 255 | if (is_local) { |
| 256 | SYSTEMTIME utc_st; |
| 257 | // We don't use FileTimeToLocalFileTime here, since it uses the current |
| 258 | // settings for the time zone and daylight saving time. Therefore, if it is |
| 259 | // daylight saving time, it will take daylight saving time into account, |
| 260 | // even if the time you are converting is in standard time. |
| 261 | success = FileTimeToSystemTime(&utc_ft, &utc_st) && |
| 262 | SystemTimeToTzSpecificLocalTime(NULL, &utc_st, &st); |
| 263 | } else { |
| 264 | success = !!FileTimeToSystemTime(&utc_ft, &st); |
| 265 | } |
| 266 | |
| 267 | if (!success) { |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 268 | NOTREACHED() << "Unable to convert time, don't know why"; |
[email protected] | 74895f21 | 2011-05-21 21:55:13 | [diff] [blame] | 269 | ZeroMemory(exploded, sizeof(*exploded)); |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 270 | return; |
| 271 | } |
| 272 | |
| 273 | exploded->year = st.wYear; |
| 274 | exploded->month = st.wMonth; |
| 275 | exploded->day_of_week = st.wDayOfWeek; |
| 276 | exploded->day_of_month = st.wDay; |
| 277 | exploded->hour = st.wHour; |
| 278 | exploded->minute = st.wMinute; |
| 279 | exploded->second = st.wSecond; |
| 280 | exploded->millisecond = st.wMilliseconds; |
| 281 | } |
| 282 | |
| 283 | // TimeTicks ------------------------------------------------------------------ |
[email protected] | c5324208 | 2008-09-09 08:58:51 | [diff] [blame] | 284 | namespace { |
[email protected] | 87b3252 | 2008-09-02 12:08:36 | [diff] [blame] | 285 | |
[email protected] | c5324208 | 2008-09-09 08:58:51 | [diff] [blame] | 286 | // We define a wrapper to adapt between the __stdcall and __cdecl call of the |
| 287 | // mock function, and to avoid a static constructor. Assigning an import to a |
| 288 | // function pointer directly would require setup code to fetch from the IAT. |
| 289 | DWORD timeGetTimeWrapper() { |
| 290 | return timeGetTime(); |
[email protected] | 87b3252 | 2008-09-02 12:08:36 | [diff] [blame] | 291 | } |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 292 | |
[email protected] | c5324208 | 2008-09-09 08:58:51 | [diff] [blame] | 293 | DWORD (*tick_function)(void) = &timeGetTimeWrapper; |
| 294 | |
[email protected] | 11a5a8f | 2010-05-11 11:04:51 | [diff] [blame] | 295 | // Accumulation of time lost due to rollover (in milliseconds). |
| 296 | int64 rollover_ms = 0; |
| 297 | |
| 298 | // The last timeGetTime value we saw, to detect rollover. |
| 299 | DWORD last_seen_now = 0; |
| 300 | |
| 301 | // Lock protecting rollover_ms and last_seen_now. |
| 302 | // Note: this is a global object, and we usually avoid these. However, the time |
| 303 | // code is low-level, and we don't want to use Singletons here (it would be too |
| 304 | // easy to use a Singleton without even knowing it, and that may lead to many |
| 305 | // gotchas). Its impact on startup time should be negligible due to low-level |
| 306 | // nature of time code. |
[email protected] | 20305ec | 2011-01-21 04:55:52 | [diff] [blame] | 307 | base::Lock rollover_lock; |
[email protected] | 11a5a8f | 2010-05-11 11:04:51 | [diff] [blame] | 308 | |
[email protected] | c5324208 | 2008-09-09 08:58:51 | [diff] [blame] | 309 | // We use timeGetTime() to implement TimeTicks::Now(). This can be problematic |
| 310 | // because it returns the number of milliseconds since Windows has started, |
| 311 | // which will roll over the 32-bit value every ~49 days. We try to track |
| 312 | // rollover ourselves, which works if TimeTicks::Now() is called at least every |
| 313 | // 49 days. |
[email protected] | 11a5a8f | 2010-05-11 11:04:51 | [diff] [blame] | 314 | TimeDelta RolloverProtectedNow() { |
[email protected] | 20305ec | 2011-01-21 04:55:52 | [diff] [blame] | 315 | base::AutoLock locked(rollover_lock); |
[email protected] | 11a5a8f | 2010-05-11 11:04:51 | [diff] [blame] | 316 | // We should hold the lock while calling tick_function to make sure that |
| 317 | // we keep last_seen_now stay correctly in sync. |
| 318 | DWORD now = tick_function(); |
| 319 | if (now < last_seen_now) |
| 320 | rollover_ms += 0x100000000I64; // ~49.7 days. |
| 321 | last_seen_now = now; |
| 322 | return TimeDelta::FromMilliseconds(now + rollover_ms); |
| 323 | } |
[email protected] | c5324208 | 2008-09-09 08:58:51 | [diff] [blame] | 324 | |
initial.commit | d7cae12 | 2008-07-26 21:49:38 | [diff] [blame] | 325 | // Overview of time counters: |
| 326 | // (1) CPU cycle counter. (Retrieved via RDTSC) |
| 327 | // The CPU counter provides the highest resolution time stamp and is the least |
| 328 | // expensive to retrieve. However, the CPU counter is unreliable and should not |
| 329 | // be used in production. Its biggest issue is that it is per processor and it |
| 330 | // is not synchronized between processors. Also, on some computers, the counters |
| 331 | // will change frequency due to thermal and power changes, and stop in some |
| 332 | // states. |
| 333 | // |
| 334 | // (2) QueryPerformanceCounter (QPC). The QPC counter provides a high- |
| 335 | // resolution (100 nanoseconds) time stamp but is comparatively more expensive |
| 336 | // to retrieve. What QueryPerformanceCounter actually does is up to the HAL. |
| 337 | // (with some help from ACPI). |
| 338 | // According to http://blogs.msdn.com/oldnewthing/archive/2005/09/02/459952.aspx |
| 339 | // in the worst case, it gets the counter from the rollover interrupt on the |
| 340 | // programmable interrupt timer. In best cases, the HAL may conclude that the |
| 341 | // RDTSC counter runs at a constant frequency, then it uses that instead. On |
| 342 | // multiprocessor machines, it will try to verify the values returned from |
| 343 | // RDTSC on each processor are consistent with each other, and apply a handful |
| 344 | // of workarounds for known buggy hardware. In other words, QPC is supposed to |
| 345 | // give consistent result on a multiprocessor computer, but it is unreliable in |
| 346 | // reality due to bugs in BIOS or HAL on some, especially old computers. |
| 347 | // With recent updates on HAL and newer BIOS, QPC is getting more reliable but |
| 348 | // it should be used with caution. |
| 349 | // |
| 350 | // (3) System time. The system time provides a low-resolution (typically 10ms |
| 351 | // to 55 milliseconds) time stamp but is comparatively less expensive to |
| 352 | // retrieve and more reliable. |
[email protected] | de592d3 | 2008-09-26 03:00:00 | [diff] [blame] | 353 | class HighResNowSingleton { |
[email protected] | c5324208 | 2008-09-09 08:58:51 | [diff] [blame] | 354 | public: |
[email protected] | 864b558 | 2010-12-04 23:00:10 | [diff] [blame] | 355 | static HighResNowSingleton* GetInstance() { |
| 356 | return Singleton<HighResNowSingleton>::get(); |
[email protected] | 5f6eee53 | 2008-09-02 08:28:37 | [diff] [blame] | 357 | } |
| 358 | |
[email protected] | de592d3 | 2008-09-26 03:00:00 | [diff] [blame] | 359 | bool IsUsingHighResClock() { |
[email protected] | 61106397 | 2012-08-03 07:22:52 | [diff] [blame] | 360 | return ticks_per_second_ != 0.0; |
[email protected] | de592d3 | 2008-09-26 03:00:00 | [diff] [blame] | 361 | } |
| 362 | |
| 363 | void DisableHighResClock() { |
[email protected] | 61106397 | 2012-08-03 07:22:52 | [diff] [blame] | 364 | ticks_per_second_ = 0.0; |
[email protected] | c5324208 | 2008-09-09 08:58:51 | [diff] [blame] | 365 | } |
| 366 | |
| 367 | TimeDelta Now() { |
[email protected] | 0b2a2f4 | 2010-08-04 22:17:18 | [diff] [blame] | 368 | if (IsUsingHighResClock()) |
| 369 | return TimeDelta::FromMicroseconds(UnreliableNow()); |
[email protected] | de592d3 | 2008-09-26 03:00:00 | [diff] [blame] | 370 | |
| 371 | // Just fallback to the slower clock. |
[email protected] | 11a5a8f | 2010-05-11 11:04:51 | [diff] [blame] | 372 | return RolloverProtectedNow(); |
[email protected] | c5324208 | 2008-09-09 08:58:51 | [diff] [blame] | 373 | } |
| 374 | |
[email protected] | 0b2a2f4 | 2010-08-04 22:17:18 | [diff] [blame] | 375 | int64 GetQPCDriftMicroseconds() { |
| 376 | if (!IsUsingHighResClock()) |
| 377 | return 0; |
| 378 | |
[email protected] | ce9ca28e1 | 2011-09-20 21:46:48 | [diff] [blame] | 379 | // The static_cast<long> is needed as a hint to VS 2008 to tell it |
| 380 | // which version of abs() to use. Other compilers don't seem to |
| 381 | // need it, including VS 2010, but to keep code identical we use it |
| 382 | // everywhere. |
| 383 | // TODO(joi): Remove the hint if/when we no longer support VS 2008. |
| 384 | return abs(static_cast<long>((UnreliableNow() - ReliableNow()) - skew_)); |
[email protected] | 0b2a2f4 | 2010-08-04 22:17:18 | [diff] [blame] | 385 | } |
| 386 | |
[email protected] | 61106397 | 2012-08-03 07:22:52 | [diff] [blame] | 387 | int64 QPCValueToMicroseconds(LONGLONG qpc_value) { |
| 388 | if (!ticks_per_second_) |
| 389 | return 0; |
| 390 | |
| 391 | // Intentionally calculate microseconds in a round about manner to avoid |
| 392 | // overflow and precision issues. Think twice before simplifying! |
| 393 | int64 whole_seconds = qpc_value / ticks_per_second_; |
| 394 | int64 leftover_ticks = qpc_value % ticks_per_second_; |
| 395 | int64 microseconds = (whole_seconds * Time::kMicrosecondsPerSecond) + |
| 396 | ((leftover_ticks * Time::kMicrosecondsPerSecond) / |
| 397 | ticks_per_second_); |
| 398 | return microseconds; |
| 399 | } |
| 400 | |
[email protected] | c5324208 | 2008-09-09 08:58:51 | [diff] [blame] | 401 | private: |
[email protected] | 864b558 | 2010-12-04 23:00:10 | [diff] [blame] | 402 | HighResNowSingleton() |
[email protected] | 61106397 | 2012-08-03 07:22:52 | [diff] [blame] | 403 | : ticks_per_second_(0), |
[email protected] | 864b558 | 2010-12-04 23:00:10 | [diff] [blame] | 404 | skew_(0) { |
| 405 | InitializeClock(); |
| 406 | |
| 407 | // On Athlon X2 CPUs (e.g. model 15) QueryPerformanceCounter is |
| 408 | // unreliable. Fallback to low-res clock. |
| 409 | base::CPU cpu; |
| 410 | if (cpu.vendor_name() == "AuthenticAMD" && cpu.family() == 15) |
| 411 | DisableHighResClock(); |
| 412 | } |
| 413 | |
[email protected] | de592d3 | 2008-09-26 03:00:00 | [diff] [blame] | 414 | // Synchronize the QPC clock with GetSystemTimeAsFileTime. |
| 415 | void InitializeClock() { |
| 416 | LARGE_INTEGER ticks_per_sec = {0}; |
| 417 | if (!QueryPerformanceFrequency(&ticks_per_sec)) |
| 418 | return; // Broken, we don't guarantee this function works. |
[email protected] | 61106397 | 2012-08-03 07:22:52 | [diff] [blame] | 419 | ticks_per_second_ = ticks_per_sec.QuadPart; |
[email protected] | de592d3 | 2008-09-26 03:00:00 | [diff] [blame] | 420 | |
| 421 | skew_ = UnreliableNow() - ReliableNow(); |
| 422 | } |
| 423 | |
[email protected] | 11a5a8f | 2010-05-11 11:04:51 | [diff] [blame] | 424 | // Get the number of microseconds since boot in an unreliable fashion. |
[email protected] | de592d3 | 2008-09-26 03:00:00 | [diff] [blame] | 425 | int64 UnreliableNow() { |
| 426 | LARGE_INTEGER now; |
| 427 | QueryPerformanceCounter(&now); |
[email protected] | 61106397 | 2012-08-03 07:22:52 | [diff] [blame] | 428 | return QPCValueToMicroseconds(now.QuadPart); |
[email protected] | de592d3 | 2008-09-26 03:00:00 | [diff] [blame] | 429 | } |
| 430 | |
[email protected] | 11a5a8f | 2010-05-11 11:04:51 | [diff] [blame] | 431 | // Get the number of microseconds since boot in a reliable fashion. |
[email protected] | de592d3 | 2008-09-26 03:00:00 | [diff] [blame] | 432 | int64 ReliableNow() { |
[email protected] | 11a5a8f | 2010-05-11 11:04:51 | [diff] [blame] | 433 | return RolloverProtectedNow().InMicroseconds(); |
[email protected] | de592d3 | 2008-09-26 03:00:00 | [diff] [blame] | 434 | } |
| 435 | |
[email protected] | 61106397 | 2012-08-03 07:22:52 | [diff] [blame] | 436 | int64 ticks_per_second_; // 0 indicates QPF failed and we're broken. |
[email protected] | de592d3 | 2008-09-26 03:00:00 | [diff] [blame] | 437 | int64 skew_; // Skew between lo-res and hi-res clocks (for debugging). |
[email protected] | c5324208 | 2008-09-09 08:58:51 | [diff] [blame] | 438 | |
[email protected] | 864b558 | 2010-12-04 23:00:10 | [diff] [blame] | 439 | friend struct DefaultSingletonTraits<HighResNowSingleton>; |
[email protected] | c5324208 | 2008-09-09 08:58:51 | [diff] [blame] | 440 | }; |
| 441 | |
| 442 | } // namespace |
| 443 | |
| 444 | // static |
| 445 | TimeTicks::TickFunctionType TimeTicks::SetMockTickFunction( |
| 446 | TickFunctionType ticker) { |
| 447 | TickFunctionType old = tick_function; |
| 448 | tick_function = ticker; |
| 449 | return old; |
[email protected] | 5f6eee53 | 2008-09-02 08:28:37 | [diff] [blame] | 450 | } |
[email protected] | 87b3252 | 2008-09-02 12:08:36 | [diff] [blame] | 451 | |
[email protected] | c5324208 | 2008-09-09 08:58:51 | [diff] [blame] | 452 | // static |
| 453 | TimeTicks TimeTicks::Now() { |
[email protected] | 11a5a8f | 2010-05-11 11:04:51 | [diff] [blame] | 454 | return TimeTicks() + RolloverProtectedNow(); |
[email protected] | c5324208 | 2008-09-09 08:58:51 | [diff] [blame] | 455 | } |
| 456 | |
| 457 | // static |
[email protected] | de592d3 | 2008-09-26 03:00:00 | [diff] [blame] | 458 | TimeTicks TimeTicks::HighResNow() { |
[email protected] | 864b558 | 2010-12-04 23:00:10 | [diff] [blame] | 459 | return TimeTicks() + HighResNowSingleton::GetInstance()->Now(); |
[email protected] | c5324208 | 2008-09-09 08:58:51 | [diff] [blame] | 460 | } |
[email protected] | 0b2a2f4 | 2010-08-04 22:17:18 | [diff] [blame] | 461 | |
| 462 | // static |
[email protected] | 190f13f7 | 2012-05-01 20:56:43 | [diff] [blame] | 463 | TimeTicks TimeTicks::NowFromSystemTraceTime() { |
| 464 | return HighResNow(); |
| 465 | } |
| 466 | |
| 467 | // static |
[email protected] | 0b2a2f4 | 2010-08-04 22:17:18 | [diff] [blame] | 468 | int64 TimeTicks::GetQPCDriftMicroseconds() { |
[email protected] | 864b558 | 2010-12-04 23:00:10 | [diff] [blame] | 469 | return HighResNowSingleton::GetInstance()->GetQPCDriftMicroseconds(); |
[email protected] | 8af6f334 | 2010-09-23 15:54:48 | [diff] [blame] | 470 | } |
| 471 | |
| 472 | // static |
[email protected] | 61106397 | 2012-08-03 07:22:52 | [diff] [blame] | 473 | TimeTicks TimeTicks::FromQPCValue(LONGLONG qpc_value) { |
| 474 | return TimeTicks( |
| 475 | HighResNowSingleton::GetInstance()->QPCValueToMicroseconds(qpc_value)); |
| 476 | } |
| 477 | |
| 478 | // static |
[email protected] | 8af6f334 | 2010-09-23 15:54:48 | [diff] [blame] | 479 | bool TimeTicks::IsHighResClockWorking() { |
[email protected] | 864b558 | 2010-12-04 23:00:10 | [diff] [blame] | 480 | return HighResNowSingleton::GetInstance()->IsUsingHighResClock(); |
[email protected] | 20305ec | 2011-01-21 04:55:52 | [diff] [blame] | 481 | } |
[email protected] | 61106397 | 2012-08-03 07:22:52 | [diff] [blame] | 482 | |
| 483 | // TimeDelta ------------------------------------------------------------------ |
| 484 | |
| 485 | // static |
| 486 | TimeDelta TimeDelta::FromQPCValue(LONGLONG qpc_value) { |
| 487 | return TimeDelta( |
| 488 | HighResNowSingleton::GetInstance()->QPCValueToMicroseconds(qpc_value)); |
| 489 | } |