Chromium Code Reviews| Index: base/time/time_win.cc | 
| diff --git a/base/time/time_win.cc b/base/time/time_win.cc | 
| index 9b4f17d6613c7e60b8576adc9ae3b2a6afc8c8a3..8ae76404f0a3c1b42aeb71545758de2892b1dd5f 100644 | 
| --- a/base/time/time_win.cc | 
| +++ b/base/time/time_win.cc | 
| @@ -307,13 +307,13 @@ DWORD timeGetTimeWrapper() { | 
| return timeGetTime(); | 
| } | 
| -DWORD (*tick_function)(void) = &timeGetTimeWrapper; | 
| +DWORD (*g_tick_function)(void) = &timeGetTimeWrapper; | 
| // Accumulation of time lost due to rollover (in milliseconds). | 
| -int64 rollover_ms = 0; | 
| +int64 g_rollover_ms = 0; | 
| // The last timeGetTime value we saw, to detect rollover. | 
| -DWORD last_seen_now = 0; | 
| +DWORD g_last_seen_now = 0; | 
| // Lock protecting rollover_ms and last_seen_now. | 
| // Note: this is a global object, and we usually avoid these. However, the time | 
| @@ -321,169 +321,161 @@ DWORD last_seen_now = 0; | 
| // easy to use a Singleton without even knowing it, and that may lead to many | 
| // gotchas). Its impact on startup time should be negligible due to low-level | 
| // nature of time code. | 
| -base::Lock rollover_lock; | 
| +base::Lock g_rollover_lock; | 
| // We use timeGetTime() to implement TimeTicks::Now(). This can be problematic | 
| // because it returns the number of milliseconds since Windows has started, | 
| // which will roll over the 32-bit value every ~49 days. We try to track | 
| // rollover ourselves, which works if TimeTicks::Now() is called at least every | 
| // 49 days. | 
| -TimeDelta RolloverProtectedNow() { | 
| - base::AutoLock locked(rollover_lock); | 
| +TimeTicks RolloverProtectedNow() { | 
| + base::AutoLock locked(g_rollover_lock); | 
| // We should hold the lock while calling tick_function to make sure that | 
| // we keep last_seen_now stay correctly in sync. | 
| - DWORD now = tick_function(); | 
| - if (now < last_seen_now) | 
| - rollover_ms += 0x100000000I64; // ~49.7 days. | 
| - last_seen_now = now; | 
| - return TimeDelta::FromMilliseconds(now + rollover_ms); | 
| + DWORD now = g_tick_function(); | 
| + if (now < g_last_seen_now) | 
| + g_rollover_ms += 0x100000000I64; // ~49.7 days. | 
| + g_last_seen_now = now; | 
| + return TimeTicks() + TimeDelta::FromMilliseconds(now + g_rollover_ms); | 
| } | 
| -bool IsBuggyAthlon(const base::CPU& cpu) { | 
| - // On Athlon X2 CPUs (e.g. model 15) QueryPerformanceCounter is | 
| - // unreliable. Fallback to low-res clock. | 
| - return cpu.vendor_name() == "AuthenticAMD" && cpu.family() == 15; | 
| -} | 
| - | 
| -// Overview of time counters: | 
| +// Discussion of tick counter options on Windows: | 
| +// | 
| // (1) CPU cycle counter. (Retrieved via RDTSC) | 
| // The CPU counter provides the highest resolution time stamp and is the least | 
| -// expensive to retrieve. However, the CPU counter is unreliable and should not | 
| -// be used in production. Its biggest issue is that it is per processor and it | 
| -// is not synchronized between processors. Also, on some computers, the counters | 
| -// will change frequency due to thermal and power changes, and stop in some | 
| -// states. | 
| +// expensive to retrieve. However, on older CPUs, two issues can affect its | 
| +// reliability: First it is maintained per processor and not synchronized | 
| +// between processors. Also, the counters will change frequency due to thermal | 
| +// and power changes, and stop in some states. | 
| // | 
| // (2) QueryPerformanceCounter (QPC). The QPC counter provides a high- | 
| -// resolution (100 nanoseconds) time stamp but is comparatively more expensive | 
| -// to retrieve. What QueryPerformanceCounter actually does is up to the HAL. | 
| -// (with some help from ACPI). | 
| -// According to http://blogs.msdn.com/oldnewthing/archive/2005/09/02/459952.aspx | 
| -// in the worst case, it gets the counter from the rollover interrupt on the | 
| +// resolution (<1 microsecond) time stamp. On most hardware running today, it | 
| +// auto-detects and uses the constant-rate RDTSC counter to provide extremely | 
| +// efficient and reliable time stamps. | 
| +// | 
| +// On older CPUs where RDTSC is unreliable, it falls back to using more | 
| +// expensive (20X to 40X more costly) alternate clocks, such as HPET or the ACPI | 
| +// PM timer, and can involve system calls; and all this is up to the HAL (with | 
| +// some help from ACPI). According to | 
| +// http://blogs.msdn.com/oldnewthing/archive/2005/09/02/459952.aspx, in the | 
| +// worst case, it gets the counter from the rollover interrupt on the | 
| // programmable interrupt timer. In best cases, the HAL may conclude that the | 
| // RDTSC counter runs at a constant frequency, then it uses that instead. On | 
| // multiprocessor machines, it will try to verify the values returned from | 
| // RDTSC on each processor are consistent with each other, and apply a handful | 
| // of workarounds for known buggy hardware. In other words, QPC is supposed to | 
| -// give consistent result on a multiprocessor computer, but it is unreliable in | 
| -// reality due to bugs in BIOS or HAL on some, especially old computers. | 
| -// With recent updates on HAL and newer BIOS, QPC is getting more reliable but | 
| -// it should be used with caution. | 
| +// give consistent results on a multiprocessor computer, but for older CPUs it | 
| +// can be unreliable due bugs in BIOS or HAL. | 
| // | 
| -// (3) System time. The system time provides a low-resolution (typically 10ms | 
| -// to 55 milliseconds) time stamp but is comparatively less expensive to | 
| -// retrieve and more reliable. | 
| -class HighResNowSingleton { | 
| - public: | 
| - HighResNowSingleton() | 
| - : ticks_per_second_(0), | 
| - skew_(0) { | 
| - | 
| - base::CPU cpu; | 
| - if (IsBuggyAthlon(cpu)) | 
| - return; | 
| - | 
| - // Synchronize the QPC clock with GetSystemTimeAsFileTime. | 
| - LARGE_INTEGER ticks_per_sec = {0}; | 
| - if (!QueryPerformanceFrequency(&ticks_per_sec)) | 
| - return; // QPC is not available. | 
| - ticks_per_second_ = ticks_per_sec.QuadPart; | 
| - | 
| - skew_ = UnreliableNow() - ReliableNow(); | 
| - } | 
| - | 
| - bool IsUsingHighResClock() { | 
| - return ticks_per_second_ != 0; | 
| - } | 
| - | 
| - TimeDelta Now() { | 
| - if (IsUsingHighResClock()) | 
| - return TimeDelta::FromMicroseconds(UnreliableNow()); | 
| - | 
| - // Just fallback to the slower clock. | 
| - return RolloverProtectedNow(); | 
| +// (3) System time. The system time provides a low-resolution (from ~1 to ~15.6 | 
| +// milliseconds) time stamp but is comparatively less expensive to retrieve and | 
| +// more reliable. Time::EnableHighResolutionTimer() and | 
| +// Time::ActivateHighResolutionTimer() can be called to alter the resolution of | 
| +// this timer; and also other Windows applications can alter it, affecting this | 
| +// one. | 
| + | 
| +using NowFunction = TimeTicks (*)(void); | 
| + | 
| +TimeTicks InitialNowFunction(); | 
| +TimeTicks InitialSystemTraceNowFunction(); | 
| + | 
| +// See "threading notes" in InitializeNowFunctionPointers() for details on how | 
| +// concurrent reads/writes to these globals has been made safe. | 
| +NowFunction g_now_function = &InitialNowFunction; | 
| +NowFunction g_system_trace_now_function = &InitialSystemTraceNowFunction; | 
| +int64 g_qpc_ticks_per_second = 0; | 
| + | 
| +// As of January 2015, use of <atomic> is forbidden in Chromium code. This is | 
| +// what std::atomic_thread_fence does on Windows on all Intel architectures when | 
| +// the memory_order argument is anything but std::memory_order_seq_cst: | 
| +#define ATOMIC_THREAD_FENCE(memory_order) _ReadWriteBarrier(); | 
| + | 
| +TimeDelta QPCValueToTimeDelta(LONGLONG qpc_value) { | 
| + // Ensure that the assignment to |g_qpc_ticks_per_second|, made in | 
| + // InitializeNowFunctionPointers(), has happened by this point. | 
| + ATOMIC_THREAD_FENCE(memory_order_acquire); | 
| 
 
Alexander Potapenko
2016/07/05 12:20:29
There's base/atomicops.h with Acquire_Load() and R
 
 | 
| + | 
| + DCHECK_GT(g_qpc_ticks_per_second, 0); | 
| + | 
| + // If the QPC Value is below the overflow threshold, we proceed with | 
| + // simple multiply and divide. | 
| + if (qpc_value < Time::kQPCOverflowThreshold) { | 
| + return TimeDelta::FromMicroseconds( | 
| + qpc_value * Time::kMicrosecondsPerSecond / g_qpc_ticks_per_second); | 
| } | 
| + // Otherwise, calculate microseconds in a round about manner to avoid | 
| + // overflow and precision issues. | 
| + int64 whole_seconds = qpc_value / g_qpc_ticks_per_second; | 
| + int64 leftover_ticks = qpc_value - (whole_seconds * g_qpc_ticks_per_second); | 
| + return TimeDelta::FromMicroseconds( | 
| + (whole_seconds * Time::kMicrosecondsPerSecond) + | 
| + ((leftover_ticks * Time::kMicrosecondsPerSecond) / | 
| + g_qpc_ticks_per_second)); | 
| +} | 
| - int64 GetQPCDriftMicroseconds() { | 
| - if (!IsUsingHighResClock()) | 
| - return 0; | 
| - return abs((UnreliableNow() - ReliableNow()) - skew_); | 
| - } | 
| - | 
| - int64 QPCValueToMicroseconds(LONGLONG qpc_value) { | 
| - if (!ticks_per_second_) | 
| - return 0; | 
| - // If the QPC Value is below the overflow threshold, we proceed with | 
| - // simple multiply and divide. | 
| - if (qpc_value < Time::kQPCOverflowThreshold) | 
| - return qpc_value * Time::kMicrosecondsPerSecond / ticks_per_second_; | 
| - // Otherwise, calculate microseconds in a round about manner to avoid | 
| - // overflow and precision issues. | 
| - int64 whole_seconds = qpc_value / ticks_per_second_; | 
| - int64 leftover_ticks = qpc_value - (whole_seconds * ticks_per_second_); | 
| - int64 microseconds = (whole_seconds * Time::kMicrosecondsPerSecond) + | 
| - ((leftover_ticks * Time::kMicrosecondsPerSecond) / | 
| - ticks_per_second_); | 
| - return microseconds; | 
| - } | 
| - | 
| - private: | 
| - // Get the number of microseconds since boot in an unreliable fashion. | 
| - int64 UnreliableNow() { | 
| - LARGE_INTEGER now; | 
| - QueryPerformanceCounter(&now); | 
| - return QPCValueToMicroseconds(now.QuadPart); | 
| - } | 
| - | 
| - // Get the number of microseconds since boot in a reliable fashion. | 
| - int64 ReliableNow() { | 
| - return RolloverProtectedNow().InMicroseconds(); | 
| - } | 
| - | 
| - int64 ticks_per_second_; // 0 indicates QPF failed and we're broken. | 
| - int64 skew_; // Skew between lo-res and hi-res clocks (for debugging). | 
| -}; | 
| - | 
| -static base::LazyInstance<HighResNowSingleton>::Leaky | 
| - leaky_high_res_now_singleton = LAZY_INSTANCE_INITIALIZER; | 
| - | 
| -HighResNowSingleton* GetHighResNowSingleton() { | 
| - return leaky_high_res_now_singleton.Pointer(); | 
| +TimeTicks QPCNow() { | 
| + LARGE_INTEGER now; | 
| + QueryPerformanceCounter(&now); | 
| + return TimeTicks() + QPCValueToTimeDelta(now.QuadPart); | 
| } | 
| -TimeDelta HighResNowWrapper() { | 
| - return GetHighResNowSingleton()->Now(); | 
| +bool IsBuggyAthlon(const base::CPU& cpu) { | 
| + // On Athlon X2 CPUs (e.g. model 15) QueryPerformanceCounter is unreliable. | 
| + return cpu.vendor_name() == "AuthenticAMD" && cpu.family() == 15; | 
| } | 
| -typedef TimeDelta (*NowFunction)(void); | 
| +void InitializeNowFunctionPointers() { | 
| + LARGE_INTEGER ticks_per_sec = {0}; | 
| + if (!QueryPerformanceFrequency(&ticks_per_sec)) | 
| + ticks_per_sec.QuadPart = 0; | 
| -bool CPUReliablySupportsHighResTime() { | 
| + // If Windows cannot provide a QPC implementation, both Now() and | 
| + // NowFromSystemTraceTime() must use the low-resolution clock. | 
| + // | 
| + // If the QPC implementation is expensive and/or unreliable, Now() will use | 
| + // the low-resolution clock, but NowFromSystemTraceTime() will use the QPC (in | 
| + // the hope that it is still useful for tracing purposes). A CPU lacking a | 
| + // non-stop time counter will cause Windows to provide an alternate QPC | 
| + // implementation that works, but is expensive to use. Certain Athlon CPUs are | 
| + // known to make the QPC implementation unreliable. | 
| + // | 
| + // Otherwise, both Now functions can use the high-resolution QPC clock. As of | 
| + // 4 January 2015, ~68% of users fall within this category. | 
| + NowFunction now_function; | 
| + NowFunction system_trace_now_function; | 
| base::CPU cpu; | 
| - if (!cpu.has_non_stop_time_stamp_counter() || | 
| - !GetHighResNowSingleton()->IsUsingHighResClock()) | 
| - return false; | 
| - | 
| - if (IsBuggyAthlon(cpu)) | 
| - return false; | 
| + if (ticks_per_sec.QuadPart <= 0) { | 
| + now_function = system_trace_now_function = &RolloverProtectedNow; | 
| + } else if (!cpu.has_non_stop_time_stamp_counter() || IsBuggyAthlon(cpu)) { | 
| + now_function = &RolloverProtectedNow; | 
| + system_trace_now_function = &QPCNow; | 
| + } else { | 
| + now_function = system_trace_now_function = &QPCNow; | 
| + } | 
| - return true; | 
| + // Threading note 1: In an unlikely race condition, it's possible for two or | 
| + // more threads to enter InitializeNowFunctionPointers() in parallel. This is | 
| + // not a problem since all threads should end up writing out the same values | 
| + // to the global variables. | 
| + // | 
| + // Threading note 2: A release fence is placed here to ensure, from the | 
| + // perspective of other threads using the function pointers, that the | 
| + // assignment to |g_qpc_ticks_per_second| happens before the function pointers | 
| + // are changed. | 
| + g_qpc_ticks_per_second = ticks_per_sec.QuadPart; | 
| + ATOMIC_THREAD_FENCE(memory_order_release); | 
| + g_now_function = now_function; | 
| + g_system_trace_now_function = system_trace_now_function; | 
| } | 
| -TimeDelta InitialNowFunction(); | 
| - | 
| -volatile NowFunction now_function = InitialNowFunction; | 
| +TimeTicks InitialNowFunction() { | 
| + InitializeNowFunctionPointers(); | 
| + return g_now_function(); | 
| +} | 
| -TimeDelta InitialNowFunction() { | 
| - if (!CPUReliablySupportsHighResTime()) { | 
| - InterlockedExchangePointer( | 
| - reinterpret_cast<void* volatile*>(&now_function), | 
| - &RolloverProtectedNow); | 
| - return RolloverProtectedNow(); | 
| - } | 
| - InterlockedExchangePointer( | 
| - reinterpret_cast<void* volatile*>(&now_function), | 
| - &HighResNowWrapper); | 
| - return HighResNowWrapper(); | 
| +TimeTicks InitialSystemTraceNowFunction() { | 
| + InitializeNowFunctionPointers(); | 
| + return g_system_trace_now_function(); | 
| } | 
| } // namespace | 
| @@ -491,27 +483,24 @@ TimeDelta InitialNowFunction() { | 
| // static | 
| TimeTicks::TickFunctionType TimeTicks::SetMockTickFunction( | 
| TickFunctionType ticker) { | 
| - base::AutoLock locked(rollover_lock); | 
| - TickFunctionType old = tick_function; | 
| - tick_function = ticker; | 
| - rollover_ms = 0; | 
| - last_seen_now = 0; | 
| + base::AutoLock locked(g_rollover_lock); | 
| + TickFunctionType old = g_tick_function; | 
| + g_tick_function = ticker; | 
| + g_rollover_ms = 0; | 
| + g_last_seen_now = 0; | 
| return old; | 
| } | 
| // static | 
| TimeTicks TimeTicks::Now() { | 
| - return TimeTicks() + now_function(); | 
| + return g_now_function(); | 
| } | 
| // static | 
| -TimeTicks TimeTicks::HighResNow() { | 
| - return TimeTicks() + HighResNowWrapper(); | 
| -} | 
| - | 
| -// static | 
| -bool TimeTicks::IsHighResNowFastAndReliable() { | 
| - return CPUReliablySupportsHighResTime(); | 
| +bool TimeTicks::IsHighResolution() { | 
| + if (g_now_function == &InitialNowFunction) | 
| + InitializeNowFunctionPointers(); | 
| + return g_now_function == &QPCNow; | 
| } | 
| // static | 
| @@ -522,27 +511,17 @@ TimeTicks TimeTicks::ThreadNow() { | 
| // static | 
| TimeTicks TimeTicks::NowFromSystemTraceTime() { | 
| - return HighResNow(); | 
| -} | 
| - | 
| -// static | 
| -int64 TimeTicks::GetQPCDriftMicroseconds() { | 
| - return GetHighResNowSingleton()->GetQPCDriftMicroseconds(); | 
| + return g_system_trace_now_function(); | 
| } | 
| // static | 
| TimeTicks TimeTicks::FromQPCValue(LONGLONG qpc_value) { | 
| - return TimeTicks(GetHighResNowSingleton()->QPCValueToMicroseconds(qpc_value)); | 
| -} | 
| - | 
| -// static | 
| -bool TimeTicks::IsHighResClockWorking() { | 
| - return GetHighResNowSingleton()->IsUsingHighResClock(); | 
| + return TimeTicks() + QPCValueToTimeDelta(qpc_value); | 
| } | 
| // TimeDelta ------------------------------------------------------------------ | 
| // static | 
| TimeDelta TimeDelta::FromQPCValue(LONGLONG qpc_value) { | 
| - return TimeDelta(GetHighResNowSingleton()->QPCValueToMicroseconds(qpc_value)); | 
| + return QPCValueToTimeDelta(qpc_value); | 
| } |