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 |