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 364 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
375 // more reliable. Time::EnableHighResolutionTimer() and | 375 // more reliable. Time::EnableHighResolutionTimer() and |
376 // Time::ActivateHighResolutionTimer() can be called to alter the resolution of | 376 // Time::ActivateHighResolutionTimer() can be called to alter the resolution of |
377 // this timer; and also other Windows applications can alter it, affecting this | 377 // this timer; and also other Windows applications can alter it, affecting this |
378 // one. | 378 // one. |
379 | 379 |
380 using NowFunction = TimeDelta (*)(void); | 380 using NowFunction = TimeDelta (*)(void); |
381 | 381 |
382 TimeDelta InitialNowFunction(); | 382 TimeDelta InitialNowFunction(); |
383 TimeDelta InitialSystemTraceNowFunction(); | 383 TimeDelta InitialSystemTraceNowFunction(); |
384 | 384 |
385 // See "threading notes" in InitializeNowFunctionPointers() for details on how | 385 // See "threading notes" in InitializeNowFunctionPointer() for details on how |
386 // concurrent reads/writes to these globals has been made safe. | 386 // concurrent reads/writes to these globals has been made safe. |
387 NowFunction g_now_function = &InitialNowFunction; | 387 NowFunction g_now_function = &InitialNowFunction; |
388 NowFunction g_system_trace_now_function = &InitialSystemTraceNowFunction; | |
389 int64 g_qpc_ticks_per_second = 0; | 388 int64 g_qpc_ticks_per_second = 0; |
390 | 389 |
391 // As of January 2015, use of <atomic> is forbidden in Chromium code. This is | 390 // As of January 2015, use of <atomic> is forbidden in Chromium code. This is |
392 // what std::atomic_thread_fence does on Windows on all Intel architectures when | 391 // what std::atomic_thread_fence does on Windows on all Intel architectures when |
393 // the memory_order argument is anything but std::memory_order_seq_cst: | 392 // the memory_order argument is anything but std::memory_order_seq_cst: |
394 #define ATOMIC_THREAD_FENCE(memory_order) _ReadWriteBarrier(); | 393 #define ATOMIC_THREAD_FENCE(memory_order) _ReadWriteBarrier(); |
395 | 394 |
396 TimeDelta QPCValueToTimeDelta(LONGLONG qpc_value) { | 395 TimeDelta QPCValueToTimeDelta(LONGLONG qpc_value) { |
397 // Ensure that the assignment to |g_qpc_ticks_per_second|, made in | 396 // Ensure that the assignment to |g_qpc_ticks_per_second|, made in |
398 // InitializeNowFunctionPointers(), has happened by this point. | 397 // InitializeNowFunctionPointer(), has happened by this point. |
399 ATOMIC_THREAD_FENCE(memory_order_acquire); | 398 ATOMIC_THREAD_FENCE(memory_order_acquire); |
400 | 399 |
401 DCHECK_GT(g_qpc_ticks_per_second, 0); | 400 DCHECK_GT(g_qpc_ticks_per_second, 0); |
402 | 401 |
403 // If the QPC Value is below the overflow threshold, we proceed with | 402 // If the QPC Value is below the overflow threshold, we proceed with |
404 // simple multiply and divide. | 403 // simple multiply and divide. |
405 if (qpc_value < Time::kQPCOverflowThreshold) { | 404 if (qpc_value < Time::kQPCOverflowThreshold) { |
406 return TimeDelta::FromMicroseconds( | 405 return TimeDelta::FromMicroseconds( |
407 qpc_value * Time::kMicrosecondsPerSecond / g_qpc_ticks_per_second); | 406 qpc_value * Time::kMicrosecondsPerSecond / g_qpc_ticks_per_second); |
408 } | 407 } |
(...skipping 11 matching lines...) Expand all Loading... |
420 LARGE_INTEGER now; | 419 LARGE_INTEGER now; |
421 QueryPerformanceCounter(&now); | 420 QueryPerformanceCounter(&now); |
422 return QPCValueToTimeDelta(now.QuadPart); | 421 return QPCValueToTimeDelta(now.QuadPart); |
423 } | 422 } |
424 | 423 |
425 bool IsBuggyAthlon(const base::CPU& cpu) { | 424 bool IsBuggyAthlon(const base::CPU& cpu) { |
426 // On Athlon X2 CPUs (e.g. model 15) QueryPerformanceCounter is unreliable. | 425 // On Athlon X2 CPUs (e.g. model 15) QueryPerformanceCounter is unreliable. |
427 return cpu.vendor_name() == "AuthenticAMD" && cpu.family() == 15; | 426 return cpu.vendor_name() == "AuthenticAMD" && cpu.family() == 15; |
428 } | 427 } |
429 | 428 |
430 void InitializeNowFunctionPointers() { | 429 void InitializeNowFunctionPointer() { |
431 LARGE_INTEGER ticks_per_sec = {}; | 430 LARGE_INTEGER ticks_per_sec = {}; |
432 if (!QueryPerformanceFrequency(&ticks_per_sec)) | 431 if (!QueryPerformanceFrequency(&ticks_per_sec)) |
433 ticks_per_sec.QuadPart = 0; | 432 ticks_per_sec.QuadPart = 0; |
434 | 433 |
435 // If Windows cannot provide a QPC implementation, both TimeTicks::Now() and | 434 // If Windows cannot provide a QPC implementation, TimeTicks::Now() must use |
436 // TraceTicks::Now() must use the low-resolution clock. | 435 // the low-resolution clock. |
437 // | 436 // |
438 // If the QPC implementation is expensive and/or unreliable, TimeTicks::Now() | 437 // If the QPC implementation is expensive and/or unreliable, TimeTicks::Now() |
439 // will use the low-resolution clock, but TraceTicks::Now() will use the QPC | 438 // will still use the low-resolution clock. A CPU lacking a non-stop time |
440 // (in the hope that it is still useful for tracing purposes). A CPU lacking a | 439 // counter will cause Windows to provide an alternate QPC implementation that |
441 // non-stop time counter will cause Windows to provide an alternate QPC | 440 // works, but is expensive to use. Certain Athlon CPUs are known to make the |
442 // implementation that works, but is expensive to use. Certain Athlon CPUs are | 441 // QPC implementation unreliable. |
443 // known to make the QPC implementation unreliable. | |
444 // | 442 // |
445 // Otherwise, both Now functions can use the high-resolution QPC clock. As of | 443 // Otherwise, Now uses the high-resolution QPC clock. As of 21 August 2015, |
446 // 4 January 2015, ~68% of users fall within this category. | 444 // ~72% of users fall within this category. |
| 445 // |
| 446 // TraceTicks::Now() always uses the same clock as TimeTicks::Now(), even |
| 447 // when the QPC exists but is expensive or unreliable. This is because we'd |
| 448 // eventually like to merge TraceTicks and TimeTicks and have one type of |
| 449 // timestamp that is reliable, monotonic, and comparable. Also, while we could |
| 450 // use the high-resolution timer for TraceTicks even when it's unreliable or |
| 451 // slow, it's easier to make tracing tools accommodate a coarse timer than |
| 452 // one that's unreliable or slow. |
447 NowFunction now_function; | 453 NowFunction now_function; |
448 NowFunction system_trace_now_function; | |
449 base::CPU cpu; | 454 base::CPU cpu; |
450 if (ticks_per_sec.QuadPart <= 0) { | 455 if (ticks_per_sec.QuadPart <= 0 || |
451 now_function = system_trace_now_function = &RolloverProtectedNow; | 456 !cpu.has_non_stop_time_stamp_counter() || IsBuggyAthlon(cpu)) { |
452 } else if (!cpu.has_non_stop_time_stamp_counter() || IsBuggyAthlon(cpu)) { | |
453 now_function = &RolloverProtectedNow; | 457 now_function = &RolloverProtectedNow; |
454 system_trace_now_function = &QPCNow; | |
455 } else { | 458 } else { |
456 now_function = system_trace_now_function = &QPCNow; | 459 now_function = &QPCNow; |
457 } | 460 } |
458 | 461 |
459 // Threading note 1: In an unlikely race condition, it's possible for two or | 462 // Threading note 1: In an unlikely race condition, it's possible for two or |
460 // more threads to enter InitializeNowFunctionPointers() in parallel. This is | 463 // more threads to enter InitializeNowFunctionPointer() in parallel. This is |
461 // not a problem since all threads should end up writing out the same values | 464 // not a problem since all threads should end up writing out the same values |
462 // to the global variables. | 465 // to the global variables. |
463 // | 466 // |
464 // Threading note 2: A release fence is placed here to ensure, from the | 467 // Threading note 2: A release fence is placed here to ensure, from the |
465 // perspective of other threads using the function pointers, that the | 468 // perspective of other threads using the function pointers, that the |
466 // assignment to |g_qpc_ticks_per_second| happens before the function pointers | 469 // assignment to |g_qpc_ticks_per_second| happens before the function pointers |
467 // are changed. | 470 // are changed. |
468 g_qpc_ticks_per_second = ticks_per_sec.QuadPart; | 471 g_qpc_ticks_per_second = ticks_per_sec.QuadPart; |
469 ATOMIC_THREAD_FENCE(memory_order_release); | 472 ATOMIC_THREAD_FENCE(memory_order_release); |
470 g_now_function = now_function; | 473 g_now_function = now_function; |
471 g_system_trace_now_function = system_trace_now_function; | |
472 } | 474 } |
473 | 475 |
474 TimeDelta InitialNowFunction() { | 476 TimeDelta InitialNowFunction() { |
475 InitializeNowFunctionPointers(); | 477 InitializeNowFunctionPointer(); |
476 return g_now_function(); | 478 return g_now_function(); |
477 } | 479 } |
478 | 480 |
479 TimeDelta InitialSystemTraceNowFunction() { | |
480 InitializeNowFunctionPointers(); | |
481 return g_system_trace_now_function(); | |
482 } | |
483 | |
484 } // namespace | 481 } // namespace |
485 | 482 |
486 // static | 483 // static |
487 TimeTicks::TickFunctionType TimeTicks::SetMockTickFunction( | 484 TimeTicks::TickFunctionType TimeTicks::SetMockTickFunction( |
488 TickFunctionType ticker) { | 485 TickFunctionType ticker) { |
489 base::AutoLock locked(g_rollover_lock); | 486 base::AutoLock locked(g_rollover_lock); |
490 TickFunctionType old = g_tick_function; | 487 TickFunctionType old = g_tick_function; |
491 g_tick_function = ticker; | 488 g_tick_function = ticker; |
492 g_rollover_ms = 0; | 489 g_rollover_ms = 0; |
493 g_last_seen_now = 0; | 490 g_last_seen_now = 0; |
494 return old; | 491 return old; |
495 } | 492 } |
496 | 493 |
497 // static | 494 // static |
498 TimeTicks TimeTicks::Now() { | 495 TimeTicks TimeTicks::Now() { |
499 return TimeTicks() + g_now_function(); | 496 return TimeTicks() + g_now_function(); |
500 } | 497 } |
501 | 498 |
502 // static | 499 // static |
503 bool TimeTicks::IsHighResolution() { | 500 bool TimeTicks::IsHighResolution() { |
504 if (g_now_function == &InitialNowFunction) | 501 if (g_now_function == &InitialNowFunction) |
505 InitializeNowFunctionPointers(); | 502 InitializeNowFunctionPointer(); |
506 return g_now_function == &QPCNow; | 503 return g_now_function == &QPCNow; |
507 } | 504 } |
508 | 505 |
509 // static | 506 // static |
510 ThreadTicks ThreadTicks::Now() { | 507 ThreadTicks ThreadTicks::Now() { |
511 NOTREACHED(); | 508 NOTREACHED(); |
512 return ThreadTicks(); | 509 return ThreadTicks(); |
513 } | 510 } |
514 | 511 |
515 // static | 512 // static |
516 TraceTicks TraceTicks::Now() { | 513 TraceTicks TraceTicks::Now() { |
517 return TraceTicks() + g_system_trace_now_function(); | 514 return TraceTicks() + g_now_function(); |
518 } | 515 } |
519 | 516 |
520 // static | 517 // static |
521 TimeTicks TimeTicks::FromQPCValue(LONGLONG qpc_value) { | 518 TimeTicks TimeTicks::FromQPCValue(LONGLONG qpc_value) { |
522 return TimeTicks() + QPCValueToTimeDelta(qpc_value); | 519 return TimeTicks() + QPCValueToTimeDelta(qpc_value); |
523 } | 520 } |
524 | 521 |
525 // TimeDelta ------------------------------------------------------------------ | 522 // TimeDelta ------------------------------------------------------------------ |
526 | 523 |
527 // static | 524 // static |
528 TimeDelta TimeDelta::FromQPCValue(LONGLONG qpc_value) { | 525 TimeDelta TimeDelta::FromQPCValue(LONGLONG qpc_value) { |
529 return QPCValueToTimeDelta(qpc_value); | 526 return QPCValueToTimeDelta(qpc_value); |
530 } | 527 } |
OLD | NEW |