From 5f6eee53a4ba6995b68a8ec6f22d3281529060c8 Mon Sep 17 00:00:00 2001 From: "deanm@google.com" Date: Tue, 2 Sep 2008 08:28:37 +0000 Subject: Cleanup some Windows TimeTicks functions. Move TickTicks::Now and TicksTicks::UnreliableHighResNow to Singletons, which cleans up the code and removes some possible unsafe cross-thread access. Move the Windows-specific rollover tests to time_unittests_win. git-svn-id: svn://svn.chromium.org/chrome/trunk/src@1633 0039d316-1c4b-4281-b951-d872f2087c98 --- base/time_win.cc | 125 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 70 insertions(+), 55 deletions(-) (limited to 'base/time_win.cc') diff --git a/base/time_win.cc b/base/time_win.cc index 9d38993..a919395 100644 --- a/base/time_win.cc +++ b/base/time_win.cc @@ -7,9 +7,11 @@ #pragma comment(lib, "winmm.lib") #include #include + #include "base/basictypes.h" #include "base/lock.h" #include "base/logging.h" +#include "base/singleton.h" namespace { @@ -122,53 +124,39 @@ void Time::Explode(bool is_local, Exploded* exploded) const { } // TimeTicks ------------------------------------------------------------------ - -TimeTicks::TickFunction TimeTicks::tick_function_= +TimeTicks::TickFunction TimeTicks::tick_function_ = reinterpret_cast(&timeGetTime); -// static -TimeTicks TimeTicks::Now() { - // Uses the multimedia timers on Windows to get a higher resolution clock. - // timeGetTime() provides a resolution which is variable depending on - // hardware and system configuration. It can also be changed by other - // apps. This class does not attempt to change the resolution of the - // timer, because we don't want to affect other applications. - - // timeGetTime() should at least be accurate to ~5ms on all systems. - // timeGetTime() returns a 32-bit millisecond counter which has rollovers - // every ~49 days. - static DWORD last_tick_count = 0; - static int64 tick_rollover_accum = 0; - static Lock* tick_lock = NULL; // To protect during rollover periods. - - // Lazily create the lock we use. - if (!tick_lock) { - Lock* new_lock = new Lock; - if (InterlockedCompareExchangePointer( - reinterpret_cast(&tick_lock), new_lock, NULL)) { - delete new_lock; - } - } +namespace { - // Atomically protect the low and high 32bit values for time. - // In the future we may be able to optimize with - // InterlockedCompareExchange64, but that doesn't work on XP. - DWORD tick_count; - int64 rollover_count; - { - AutoLock lock(*tick_lock); - tick_count = tick_function_(); - if (tick_count < last_tick_count) - tick_rollover_accum += GG_INT64_C(0x100000000); - - last_tick_count = tick_count; - rollover_count = tick_rollover_accum; +// We use timeGetTime() to implement TimeTicks::Now(). This can be problematic +// because it returns the number of millisecond 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. +class NowSingleton { + public: + NowSingleton() + : rollover_(TimeDelta::FromMilliseconds(0)), + last_seen_(TimeTicks::tick_function_()) { } + + TimeDelta Now() { + AutoLock locked(lock_); + // We should hold the lock while calling tick_function_ to make sure that + // we keep our last_seen_ stay correctly in sync. + DWORD now = TimeTicks::tick_function_(); + if (now < last_seen_) + rollover_ += TimeDelta::FromMilliseconds(0x100000000I64); // ~49.7 days. + last_seen_ = now; + return TimeDelta::FromMilliseconds(now) + rollover_; } - // GetTickCount returns milliseconds, we want microseconds. - return TimeTicks((tick_count + rollover_count) * - Time::kMicrosecondsPerMillisecond); -} + private: + Lock lock_; // To protected last_seen_ and rollover_. + TimeDelta rollover_; // Accumulation of time lost do to rollover. + DWORD last_seen_; // The last timeGetTime value we saw, to detect rollover. + + DISALLOW_COPY_AND_ASSIGN(NowSingleton); +}; // Overview of time counters: // (1) CPU cycle counter. (Retrieved via RDTSC) @@ -198,23 +186,50 @@ TimeTicks TimeTicks::Now() { // (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 UnreliableHighResNowSingleton { + public: + UnreliableHighResNowSingleton() : ticks_per_microsecond_(0) { + LARGE_INTEGER ticks_per_sec = {0}; + if (!QueryPerformanceFrequency(&ticks_per_sec)) + return; // Broken, we don't guarantee this function works. + ticks_per_microsecond_ = + ticks_per_sec.QuadPart / Time::kMicrosecondsPerSecond; + } -// static -TimeTicks TimeTicks::UnreliableHighResNow() { + bool IsBroken() { + return ticks_per_microsecond_ == 0; + } + + TimeDelta Now() { + LARGE_INTEGER now; + QueryPerformanceCounter(&now); + return TimeDelta::FromMicroseconds(now.QuadPart / ticks_per_microsecond_); + } + + private: // Cached clock frequency -> microseconds. This assumes that the clock // frequency is faster than one microsecond (which is 1MHz, should be OK). - static int64 ticks_per_microsecond = 0; + int64 ticks_per_microsecond_; // 0 indicates QPF failed and we're broken. - if (ticks_per_microsecond == 0) { - LARGE_INTEGER ticks_per_sec = { 0, 0 }; - if (!QueryPerformanceFrequency(&ticks_per_sec)) - return TimeTicks(0); // Broken, we don't guarantee this function works. - ticks_per_microsecond = - ticks_per_sec.QuadPart / Time::kMicrosecondsPerSecond; - } + DISALLOW_COPY_AND_ASSIGN(UnreliableHighResNowSingleton); +}; + +} // namespace - LARGE_INTEGER now; - QueryPerformanceCounter(&now); - return TimeTicks(now.QuadPart / ticks_per_microsecond); +// static +TimeTicks TimeTicks::Now() { + return TimeTicks() + Singleton::get()->Now(); } +// static +TimeTicks TimeTicks::UnreliableHighResNow() { + UnreliableHighResNowSingleton* now = + Singleton::get(); + + if (now->IsBroken()) { + NOTREACHED() << "QueryPerformanceCounter is broken."; + return TimeTicks(0); + } + + return TimeTicks() + now->Now(); +} -- cgit v1.1