| 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 355 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 366 // reality due to bugs in BIOS or HAL on some, especially old computers. | 366 // reality due to bugs in BIOS or HAL on some, especially old computers. |
| 367 // With recent updates on HAL and newer BIOS, QPC is getting more reliable but | 367 // With recent updates on HAL and newer BIOS, QPC is getting more reliable but |
| 368 // it should be used with caution. | 368 // it should be used with caution. |
| 369 // | 369 // |
| 370 // (3) System time. The system time provides a low-resolution (typically 10ms | 370 // (3) System time. The system time provides a low-resolution (typically 10ms |
| 371 // to 55 milliseconds) time stamp but is comparatively less expensive to | 371 // to 55 milliseconds) time stamp but is comparatively less expensive to |
| 372 // retrieve and more reliable. | 372 // retrieve and more reliable. |
| 373 class HighResNowSingleton { | 373 class HighResNowSingleton { |
| 374 public: | 374 public: |
| 375 HighResNowSingleton() | 375 HighResNowSingleton() |
| 376 : ticks_per_second_(0), | 376 : ticks_per_second_(0), |
| 377 skew_(0) { | 377 skew_(0) { |
| 378 InitializeClock(); | |
| 379 | 378 |
| 380 base::CPU cpu; | 379 base::CPU cpu; |
| 381 if (IsBuggyAthlon(cpu)) | 380 if (IsBuggyAthlon(cpu)) |
| 382 DisableHighResClock(); | 381 return; |
| 382 |
| 383 // Synchronize the QPC clock with GetSystemTimeAsFileTime. |
| 384 LARGE_INTEGER ticks_per_sec = {0}; |
| 385 if (!QueryPerformanceFrequency(&ticks_per_sec)) |
| 386 return; // QPC is not available. |
| 387 ticks_per_second_ = ticks_per_sec.QuadPart; |
| 388 |
| 389 skew_ = UnreliableNow() - ReliableNow(); |
| 383 } | 390 } |
| 384 | 391 |
| 385 bool IsUsingHighResClock() { | 392 bool IsUsingHighResClock() { |
| 386 return ticks_per_second_ != 0.0; | 393 return ticks_per_second_ != 0; |
| 387 } | |
| 388 | |
| 389 void DisableHighResClock() { | |
| 390 ticks_per_second_ = 0.0; | |
| 391 } | 394 } |
| 392 | 395 |
| 393 TimeDelta Now() { | 396 TimeDelta Now() { |
| 394 if (IsUsingHighResClock()) | 397 if (IsUsingHighResClock()) |
| 395 return TimeDelta::FromMicroseconds(UnreliableNow()); | 398 return TimeDelta::FromMicroseconds(UnreliableNow()); |
| 396 | 399 |
| 397 // Just fallback to the slower clock. | 400 // Just fallback to the slower clock. |
| 398 return RolloverProtectedNow(); | 401 return RolloverProtectedNow(); |
| 399 } | 402 } |
| 400 | 403 |
| (...skipping 14 matching lines...) Expand all Loading... |
| 415 // overflow and precision issues. | 418 // overflow and precision issues. |
| 416 int64 whole_seconds = qpc_value / ticks_per_second_; | 419 int64 whole_seconds = qpc_value / ticks_per_second_; |
| 417 int64 leftover_ticks = qpc_value - (whole_seconds * ticks_per_second_); | 420 int64 leftover_ticks = qpc_value - (whole_seconds * ticks_per_second_); |
| 418 int64 microseconds = (whole_seconds * Time::kMicrosecondsPerSecond) + | 421 int64 microseconds = (whole_seconds * Time::kMicrosecondsPerSecond) + |
| 419 ((leftover_ticks * Time::kMicrosecondsPerSecond) / | 422 ((leftover_ticks * Time::kMicrosecondsPerSecond) / |
| 420 ticks_per_second_); | 423 ticks_per_second_); |
| 421 return microseconds; | 424 return microseconds; |
| 422 } | 425 } |
| 423 | 426 |
| 424 private: | 427 private: |
| 425 // Synchronize the QPC clock with GetSystemTimeAsFileTime. | |
| 426 void InitializeClock() { | |
| 427 LARGE_INTEGER ticks_per_sec = {0}; | |
| 428 if (!QueryPerformanceFrequency(&ticks_per_sec)) | |
| 429 return; // Broken, we don't guarantee this function works. | |
| 430 ticks_per_second_ = ticks_per_sec.QuadPart; | |
| 431 | |
| 432 skew_ = UnreliableNow() - ReliableNow(); | |
| 433 } | |
| 434 | |
| 435 // Get the number of microseconds since boot in an unreliable fashion. | 428 // Get the number of microseconds since boot in an unreliable fashion. |
| 436 int64 UnreliableNow() { | 429 int64 UnreliableNow() { |
| 437 LARGE_INTEGER now; | 430 LARGE_INTEGER now; |
| 438 QueryPerformanceCounter(&now); | 431 QueryPerformanceCounter(&now); |
| 439 return QPCValueToMicroseconds(now.QuadPart); | 432 return QPCValueToMicroseconds(now.QuadPart); |
| 440 } | 433 } |
| 441 | 434 |
| 442 // Get the number of microseconds since boot in a reliable fashion. | 435 // Get the number of microseconds since boot in a reliable fashion. |
| 443 int64 ReliableNow() { | 436 int64 ReliableNow() { |
| 444 return RolloverProtectedNow().InMicroseconds(); | 437 return RolloverProtectedNow().InMicroseconds(); |
| 445 } | 438 } |
| 446 | 439 |
| 447 int64 ticks_per_second_; // 0 indicates QPF failed and we're broken. | 440 int64 ticks_per_second_; // 0 indicates QPF failed and we're broken. |
| 448 int64 skew_; // Skew between lo-res and hi-res clocks (for debugging). | 441 int64 skew_; // Skew between lo-res and hi-res clocks (for debugging). |
| 449 }; | 442 }; |
| 450 | 443 |
| 451 static base::LazyInstance<HighResNowSingleton>::Leaky | 444 static base::LazyInstance<HighResNowSingleton>::Leaky |
| 452 leaky_high_res_now_singleton = LAZY_INSTANCE_INITIALIZER; | 445 leaky_high_res_now_singleton = LAZY_INSTANCE_INITIALIZER; |
| 453 | 446 |
| 454 HighResNowSingleton* GetHighResNowSingleton() { | 447 HighResNowSingleton* GetHighResNowSingleton() { |
| 455 return leaky_high_res_now_singleton.Pointer(); | 448 return leaky_high_res_now_singleton.Pointer(); |
| 456 } | 449 } |
| 457 | 450 |
| 458 TimeDelta HighResNowWrapper() { | 451 TimeDelta HighResNowWrapper() { |
| 459 return GetHighResNowSingleton()->Now(); | 452 return GetHighResNowSingleton()->Now(); |
| 460 } | 453 } |
| 461 | 454 |
| 462 typedef TimeDelta (*NowFunction)(void); | 455 typedef TimeDelta (*NowFunction)(void); |
| 463 NowFunction now_function = RolloverProtectedNow; | |
| 464 | 456 |
| 465 bool CPUReliablySupportsHighResTime() { | 457 bool CPUReliablySupportsHighResTime() { |
| 466 base::CPU cpu; | 458 base::CPU cpu; |
| 467 if (!cpu.has_non_stop_time_stamp_counter() || | 459 if (!cpu.has_non_stop_time_stamp_counter() || |
| 468 !GetHighResNowSingleton()->IsUsingHighResClock()) | 460 !GetHighResNowSingleton()->IsUsingHighResClock()) |
| 469 return false; | 461 return false; |
| 470 | 462 |
| 471 if (IsBuggyAthlon(cpu)) | 463 if (IsBuggyAthlon(cpu)) |
| 472 return false; | 464 return false; |
| 473 | 465 |
| 474 return true; | 466 return true; |
| 475 } | 467 } |
| 476 | 468 |
| 469 TimeDelta InitialNowFunction(); |
| 470 |
| 471 NowFunction now_function = InitialNowFunction; |
| 472 |
| 473 TimeDelta InitialNowFunction() { |
| 474 if (!CPUReliablySupportsHighResTime()) { |
| 475 InterlockedExchangePointer( |
| 476 reinterpret_cast<volatile PVOID*>(&now_function), |
| 477 reinterpret_cast<PVOID>(RolloverProtectedNow)); |
| 478 return RolloverProtectedNow(); |
| 479 } |
| 480 InterlockedExchangePointer( |
| 481 reinterpret_cast<volatile PVOID*>(&now_function), |
| 482 reinterpret_cast<PVOID>(HighResNowWrapper)); |
| 483 return HighResNowWrapper(); |
| 484 } |
| 485 |
| 477 } // namespace | 486 } // namespace |
| 478 | 487 |
| 479 // static | 488 // static |
| 480 TimeTicks::TickFunctionType TimeTicks::SetMockTickFunction( | 489 TimeTicks::TickFunctionType TimeTicks::SetMockTickFunction( |
| 481 TickFunctionType ticker) { | 490 TickFunctionType ticker) { |
| 482 base::AutoLock locked(rollover_lock); | 491 base::AutoLock locked(rollover_lock); |
| 483 TickFunctionType old = tick_function; | 492 TickFunctionType old = tick_function; |
| 484 tick_function = ticker; | 493 tick_function = ticker; |
| 485 rollover_ms = 0; | 494 rollover_ms = 0; |
| 486 last_seen_now = 0; | 495 last_seen_now = 0; |
| 487 return old; | 496 return old; |
| 488 } | 497 } |
| 489 | 498 |
| 490 // static | 499 // static |
| 491 bool TimeTicks::SetNowIsHighResNowIfSupported() { | |
| 492 if (!CPUReliablySupportsHighResTime()) { | |
| 493 return false; | |
| 494 } | |
| 495 | |
| 496 now_function = HighResNowWrapper; | |
| 497 return true; | |
| 498 } | |
| 499 | |
| 500 // static | |
| 501 TimeTicks TimeTicks::Now() { | 500 TimeTicks TimeTicks::Now() { |
| 502 return TimeTicks() + now_function(); | 501 return TimeTicks() + now_function(); |
| 503 } | 502 } |
| 504 | 503 |
| 505 // static | 504 // static |
| 506 TimeTicks TimeTicks::HighResNow() { | 505 TimeTicks TimeTicks::HighResNow() { |
| 507 return TimeTicks() + HighResNowWrapper(); | 506 return TimeTicks() + HighResNowWrapper(); |
| 508 } | 507 } |
| 509 | 508 |
| 510 // static | 509 // static |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 545 return TimeTicks() + TimeDelta::FromMilliseconds(timeGetTime()); | 544 return TimeTicks() + TimeDelta::FromMilliseconds(timeGetTime()); |
| 546 } | 545 } |
| 547 } | 546 } |
| 548 | 547 |
| 549 // TimeDelta ------------------------------------------------------------------ | 548 // TimeDelta ------------------------------------------------------------------ |
| 550 | 549 |
| 551 // static | 550 // static |
| 552 TimeDelta TimeDelta::FromQPCValue(LONGLONG qpc_value) { | 551 TimeDelta TimeDelta::FromQPCValue(LONGLONG qpc_value) { |
| 553 return TimeDelta(GetHighResNowSingleton()->QPCValueToMicroseconds(qpc_value)); | 552 return TimeDelta(GetHighResNowSingleton()->QPCValueToMicroseconds(qpc_value)); |
| 554 } | 553 } |
| OLD | NEW |