| 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);
|
| +
|
| + 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);
|
| }
|
|
|