Index: base/time/time_win.cc |
diff --git a/base/time/time_win.cc b/base/time/time_win.cc |
index 9b4f17d6613c7e60b8576adc9ae3b2a6afc8c8a3..a10d80587fe2955d611fb6e90f745edbe8d19535 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,31 +321,26 @@ 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 |
@@ -373,117 +368,91 @@ bool IsBuggyAthlon(const base::CPU& cpu) { |
// (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; |
- } |
+using NowFunction = TimeTicks (*)(void); |
- TimeDelta Now() { |
- if (IsUsingHighResClock()) |
- return TimeDelta::FromMicroseconds(UnreliableNow()); |
- |
- // Just fallback to the slower clock. |
- return RolloverProtectedNow(); |
- } |
- |
- int64 GetQPCDriftMicroseconds() { |
- if (!IsUsingHighResClock()) |
- return 0; |
- return abs((UnreliableNow() - ReliableNow()) - skew_); |
- } |
+TimeTicks InitialNowFunction(); |
+TimeTicks InitialSystemTraceNowFunction(); |
- 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; |
- } |
+volatile NowFunction g_now_function = &InitialNowFunction; |
+volatile NowFunction g_system_trace_now_function = |
+ &InitialSystemTraceNowFunction; |
+int64 g_qpc_ticks_per_second = 0; |
- private: |
- // Get the number of microseconds since boot in an unreliable fashion. |
- int64 UnreliableNow() { |
- LARGE_INTEGER now; |
- QueryPerformanceCounter(&now); |
- return QPCValueToMicroseconds(now.QuadPart); |
- } |
+TimeDelta QPCValueToTimeDelta(LONGLONG qpc_value) { |
+ DCHECK_GT(g_qpc_ticks_per_second, 0); |
- // Get the number of microseconds since boot in a reliable fashion. |
- int64 ReliableNow() { |
- return RolloverProtectedNow().InMicroseconds(); |
+ // 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); |
} |
- |
- 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(); |
+ // 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)); |
} |
-TimeDelta HighResNowWrapper() { |
- return GetHighResNowSingleton()->Now(); |
+TimeTicks QPCNow() { |
+ LARGE_INTEGER now; |
+ QueryPerformanceCounter(&now); |
+ return TimeTicks() + QPCValueToTimeDelta(now.QuadPart); |
} |
-typedef TimeDelta (*NowFunction)(void); |
- |
-bool CPUReliablySupportsHighResTime() { |
+void InitializeNowFunctionPointers() { |
+ LARGE_INTEGER ticks_per_sec = {0}; |
+ if (QueryPerformanceFrequency(&ticks_per_sec)) |
+ g_qpc_ticks_per_second = ticks_per_sec.QuadPart; |
+ else |
+ g_qpc_ticks_per_second = 0; |
+ |
+ // If Windows does not offer a working QPC implementation, both Now() and |
+ // NowFromSystemTraceTime() must use the low-resolution clock. Note that |
+ // Windows may report a working QPC implementation even on certain Athlon X2 |
+ // CPUs (where QPC has been shown to be unreliable). |
+ // |
+ // Otherwise, if the CPU does not have a non-stop time counter, assume Windows |
+ // will provide an alternate QPC implementation that works, but is expensive |
+ // to use. In this case, Now() should use the inexpensive, low-resolution |
+ // clock and NowFromSystemTraceTime() will use the expensive-but-working QPC |
+ // clock. |
+ // |
+ // 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 ((g_qpc_ticks_per_second <= 0) || |
+ (cpu.vendor_name() == "AuthenticAMD" && cpu.family() == 15)) { |
+ now_function = system_trace_now_function = &RolloverProtectedNow; |
+ } else if (!cpu.has_non_stop_time_stamp_counter()) { |
+ now_function = &RolloverProtectedNow; |
+ system_trace_now_function = &QPCNow; |
+ } else { |
+ now_function = system_trace_now_function = &QPCNow; |
+ } |
- return true; |
+ InterlockedExchangePointer( |
brucedawson
2015/01/07 01:49:26
What protection is InterlockedExchangePointer supp
brianderson
2015/01/07 01:53:07
Pointers will be 32-bits on 32-bit systems, so sim
brianderson
2015/01/07 01:54:53
If we do get rid of the InterlockedExchanges, we'd
miu
2015/01/07 05:22:45
Done.
miu
2015/01/07 05:22:45
Yep. Eliminated in PS3.
miu
2015/01/07 05:22:45
There was a subtlety in the old code. Use of the
|
+ reinterpret_cast<void* volatile*>(&g_now_function), |
+ now_function); |
+ InterlockedExchangePointer( |
+ reinterpret_cast<void* volatile*>(&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 +460,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 +488,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); |
} |