Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3)

Side by Side Diff: base/time/time_win.cc

Issue 1284053004: Makes TraceTicks use lowres times on Win when highres are buggy or slow (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Improved the comments about why we're changing timer behavior Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698