Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 | 5 |
| 6 // Windows Timer Primer | 6 // Windows Timer Primer |
| 7 // | 7 // |
| 8 // A good article: http://www.ddj.com/windows/184416651 | 8 // A good article: http://www.ddj.com/windows/184416651 |
| 9 // A good mozilla bug: http://bugzilla.mozilla.org/show_bug.cgi?id=363258 | 9 // A good mozilla bug: http://bugzilla.mozilla.org/show_bug.cgi?id=363258 |
| 10 // | 10 // |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 72 | 72 |
| 73 int64 CurrentWallclockMicroseconds() { | 73 int64 CurrentWallclockMicroseconds() { |
| 74 FILETIME ft; | 74 FILETIME ft; |
| 75 ::GetSystemTimeAsFileTime(&ft); | 75 ::GetSystemTimeAsFileTime(&ft); |
| 76 return FileTimeToMicroseconds(ft); | 76 return FileTimeToMicroseconds(ft); |
| 77 } | 77 } |
| 78 | 78 |
| 79 // Time between resampling the un-granular clock for this API. 60 seconds. | 79 // Time between resampling the un-granular clock for this API. 60 seconds. |
| 80 const int kMaxMillisecondsToAvoidDrift = 60 * Time::kMillisecondsPerSecond; | 80 const int kMaxMillisecondsToAvoidDrift = 60 * Time::kMillisecondsPerSecond; |
| 81 | 81 |
| 82 int64 initial_time = 0; | 82 // The ticks at initialization. |
| 83 TimeTicks initial_ticks; | 83 TimeTicks initial_ticks; |
| 84 | 84 |
| 85 // The time at initialization. | |
| 86 int64 initial_time = 0; | |
| 87 | |
| 88 // Lock protecting initial_time and initial_ticks. | |
| 89 // Note: this is a global object, and we usually avoid these. However, the time | |
| 90 // code is low-level, and we don't want to use Singletons here (it would be too | |
|
Nico
2014/08/15 23:17:38
(fwiw, on linux or mac, "low-level" isn't an excus
willchan no longer on Chromium
2014/08/15 23:20:19
This is very true. But what's the alternative? Do
| |
| 91 // easy to use a Singleton without even knowing it, and that may lead to many | |
| 92 // gotchas). Its impact on startup time should be negligible due to low-level | |
| 93 // nature of time code. | |
| 94 base::Lock initialize_clock_lock; | |
| 95 | |
| 85 void InitializeClock() { | 96 void InitializeClock() { |
| 97 base::AutoLock locked(initialize_clock_lock); | |
|
M-A Ruel
2014/08/16 00:40:14
Because I'm a nice guy, here's the code you actual
| |
| 86 initial_ticks = TimeTicks::Now(); | 98 initial_ticks = TimeTicks::Now(); |
| 87 initial_time = CurrentWallclockMicroseconds(); | 99 initial_time = CurrentWallclockMicroseconds(); |
| 88 } | 100 } |
| 89 | 101 |
| 90 } // namespace | 102 } // namespace |
| 91 | 103 |
| 92 // Time ----------------------------------------------------------------------- | 104 // Time ----------------------------------------------------------------------- |
| 93 | 105 |
| 94 // The internal representation of Time uses FILETIME, whose epoch is 1601-01-01 | 106 // 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 | 107 // 00:00:00 UTC. ((1970-1601)*365+89)*24*60*60*1000*1000, where 89 is the |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 112 // | 124 // |
| 113 // To make this work, we initialize the clock (initial_time) and the | 125 // To make this work, we initialize the clock (initial_time) and the |
| 114 // counter (initial_ctr). To compute the initial time, we can check | 126 // counter (initial_ctr). To compute the initial time, we can check |
| 115 // the number of ticks that have elapsed, and compute the delta. | 127 // the number of ticks that have elapsed, and compute the delta. |
| 116 // | 128 // |
| 117 // To avoid any drift, we periodically resync the counters to the system | 129 // To avoid any drift, we periodically resync the counters to the system |
| 118 // clock. | 130 // clock. |
| 119 while (true) { | 131 while (true) { |
| 120 TimeTicks ticks = TimeTicks::Now(); | 132 TimeTicks ticks = TimeTicks::Now(); |
| 121 | 133 |
| 134 int64 start_time = 0; | |
| 135 TimeTicks start_ticks; | |
| 136 { | |
| 137 base::AutoLock locked(initialize_clock_lock); | |
| 138 start_time = initial_time; | |
| 139 start_ticks = initial_ticks; | |
| 140 } | |
| 141 | |
| 122 // Calculate the time elapsed since we started our timer | 142 // Calculate the time elapsed since we started our timer |
| 123 TimeDelta elapsed = ticks - initial_ticks; | 143 TimeDelta elapsed = ticks - start_ticks; |
| 124 | 144 |
| 125 // Check if enough time has elapsed that we need to resync the clock. | 145 // Check if enough time has elapsed that we need to resync the clock. |
| 126 if (elapsed.InMilliseconds() > kMaxMillisecondsToAvoidDrift) { | 146 if (elapsed.InMilliseconds() > kMaxMillisecondsToAvoidDrift) { |
| 127 InitializeClock(); | 147 InitializeClock(); |
| 128 continue; | 148 continue; |
| 129 } | 149 } |
| 130 | 150 |
| 131 return Time(elapsed + Time(initial_time)); | 151 return Time(elapsed + Time(start_time)); |
| 132 } | 152 } |
| 133 } | 153 } |
| 134 | 154 |
| 135 // static | 155 // static |
| 136 Time Time::NowFromSystemTime() { | 156 Time Time::NowFromSystemTime() { |
| 137 // Force resync. | 157 // Force resync. |
| 138 InitializeClock(); | 158 InitializeClock(); |
| 139 return Time(initial_time); | 159 return Time(initial_time); |
| 140 } | 160 } |
| 141 | 161 |
| (...skipping 210 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 352 // reality due to bugs in BIOS or HAL on some, especially old computers. | 372 // reality due to bugs in BIOS or HAL on some, especially old computers. |
| 353 // With recent updates on HAL and newer BIOS, QPC is getting more reliable but | 373 // With recent updates on HAL and newer BIOS, QPC is getting more reliable but |
| 354 // it should be used with caution. | 374 // it should be used with caution. |
| 355 // | 375 // |
| 356 // (3) System time. The system time provides a low-resolution (typically 10ms | 376 // (3) System time. The system time provides a low-resolution (typically 10ms |
| 357 // to 55 milliseconds) time stamp but is comparatively less expensive to | 377 // to 55 milliseconds) time stamp but is comparatively less expensive to |
| 358 // retrieve and more reliable. | 378 // retrieve and more reliable. |
| 359 class HighResNowSingleton { | 379 class HighResNowSingleton { |
| 360 public: | 380 public: |
| 361 HighResNowSingleton() | 381 HighResNowSingleton() |
| 362 : ticks_per_second_(0), | 382 : ticks_per_second_(0), |
| 363 skew_(0) { | 383 skew_(0) { |
| 364 InitializeClock(); | |
| 365 | 384 |
| 366 base::CPU cpu; | 385 base::CPU cpu; |
| 367 if (IsBuggyAthlon(cpu)) | 386 if (IsBuggyAthlon(cpu)) |
| 368 DisableHighResClock(); | 387 return; |
| 388 | |
| 389 // Synchronize the QPC clock with GetSystemTimeAsFileTime. | |
| 390 LARGE_INTEGER ticks_per_sec = {0}; | |
| 391 if (!QueryPerformanceFrequency(&ticks_per_sec)) | |
| 392 return; // QPC is not available. | |
| 393 ticks_per_second_ = ticks_per_sec.QuadPart; | |
| 394 | |
| 395 skew_ = UnreliableNow() - ReliableNow(); | |
| 369 } | 396 } |
| 370 | 397 |
| 371 bool IsUsingHighResClock() { | 398 bool IsUsingHighResClock() { |
| 372 return ticks_per_second_ != 0.0; | 399 return ticks_per_second_ != 0; |
| 373 } | |
| 374 | |
| 375 void DisableHighResClock() { | |
| 376 ticks_per_second_ = 0.0; | |
| 377 } | 400 } |
| 378 | 401 |
| 379 TimeDelta Now() { | 402 TimeDelta Now() { |
| 380 if (IsUsingHighResClock()) | 403 if (IsUsingHighResClock()) |
| 381 return TimeDelta::FromMicroseconds(UnreliableNow()); | 404 return TimeDelta::FromMicroseconds(UnreliableNow()); |
| 382 | 405 |
| 383 // Just fallback to the slower clock. | 406 // Just fallback to the slower clock. |
| 384 return RolloverProtectedNow(); | 407 return RolloverProtectedNow(); |
| 385 } | 408 } |
| 386 | 409 |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 401 // overflow and precision issues. | 424 // overflow and precision issues. |
| 402 int64 whole_seconds = qpc_value / ticks_per_second_; | 425 int64 whole_seconds = qpc_value / ticks_per_second_; |
| 403 int64 leftover_ticks = qpc_value - (whole_seconds * ticks_per_second_); | 426 int64 leftover_ticks = qpc_value - (whole_seconds * ticks_per_second_); |
| 404 int64 microseconds = (whole_seconds * Time::kMicrosecondsPerSecond) + | 427 int64 microseconds = (whole_seconds * Time::kMicrosecondsPerSecond) + |
| 405 ((leftover_ticks * Time::kMicrosecondsPerSecond) / | 428 ((leftover_ticks * Time::kMicrosecondsPerSecond) / |
| 406 ticks_per_second_); | 429 ticks_per_second_); |
| 407 return microseconds; | 430 return microseconds; |
| 408 } | 431 } |
| 409 | 432 |
| 410 private: | 433 private: |
| 411 // Synchronize the QPC clock with GetSystemTimeAsFileTime. | |
| 412 void InitializeClock() { | |
| 413 LARGE_INTEGER ticks_per_sec = {0}; | |
| 414 if (!QueryPerformanceFrequency(&ticks_per_sec)) | |
| 415 return; // Broken, we don't guarantee this function works. | |
| 416 ticks_per_second_ = ticks_per_sec.QuadPart; | |
| 417 | |
| 418 skew_ = UnreliableNow() - ReliableNow(); | |
| 419 } | |
| 420 | |
| 421 // Get the number of microseconds since boot in an unreliable fashion. | 434 // Get the number of microseconds since boot in an unreliable fashion. |
| 422 int64 UnreliableNow() { | 435 int64 UnreliableNow() { |
| 423 LARGE_INTEGER now; | 436 LARGE_INTEGER now; |
| 424 QueryPerformanceCounter(&now); | 437 QueryPerformanceCounter(&now); |
| 425 return QPCValueToMicroseconds(now.QuadPart); | 438 return QPCValueToMicroseconds(now.QuadPart); |
| 426 } | 439 } |
| 427 | 440 |
| 428 // Get the number of microseconds since boot in a reliable fashion. | 441 // Get the number of microseconds since boot in a reliable fashion. |
| 429 int64 ReliableNow() { | 442 int64 ReliableNow() { |
| 430 return RolloverProtectedNow().InMicroseconds(); | 443 return RolloverProtectedNow().InMicroseconds(); |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 473 return old; | 486 return old; |
| 474 } | 487 } |
| 475 | 488 |
| 476 // static | 489 // static |
| 477 bool TimeTicks::SetNowIsHighResNowIfSupported() { | 490 bool TimeTicks::SetNowIsHighResNowIfSupported() { |
| 478 if (!CPUReliablySupportsHighResTime()) { | 491 if (!CPUReliablySupportsHighResTime()) { |
| 479 return false; | 492 return false; |
| 480 } | 493 } |
| 481 | 494 |
| 482 now_function = HighResNowWrapper; | 495 now_function = HighResNowWrapper; |
| 496 InitializeClock(); | |
| 483 return true; | 497 return true; |
| 484 } | 498 } |
| 485 | 499 |
| 486 // static | 500 // static |
| 487 TimeTicks TimeTicks::Now() { | 501 TimeTicks TimeTicks::Now() { |
| 488 return TimeTicks() + now_function(); | 502 return TimeTicks() + now_function(); |
| 489 } | 503 } |
| 490 | 504 |
| 491 // static | 505 // static |
| 492 TimeTicks TimeTicks::HighResNow() { | 506 TimeTicks TimeTicks::HighResNow() { |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 531 return TimeTicks() + TimeDelta::FromMilliseconds(timeGetTime()); | 545 return TimeTicks() + TimeDelta::FromMilliseconds(timeGetTime()); |
| 532 } | 546 } |
| 533 } | 547 } |
| 534 | 548 |
| 535 // TimeDelta ------------------------------------------------------------------ | 549 // TimeDelta ------------------------------------------------------------------ |
| 536 | 550 |
| 537 // static | 551 // static |
| 538 TimeDelta TimeDelta::FromQPCValue(LONGLONG qpc_value) { | 552 TimeDelta TimeDelta::FromQPCValue(LONGLONG qpc_value) { |
| 539 return TimeDelta(GetHighResNowSingleton()->QPCValueToMicroseconds(qpc_value)); | 553 return TimeDelta(GetHighResNowSingleton()->QPCValueToMicroseconds(qpc_value)); |
| 540 } | 554 } |
| OLD | NEW |