diff options
-rw-r--r-- | base/event_recorder.cc | 10 | ||||
-rw-r--r-- | base/message_loop.cc | 18 | ||||
-rw-r--r-- | base/time.h | 5 | ||||
-rw-r--r-- | base/time_unittest_win.cc | 11 | ||||
-rw-r--r-- | base/time_win.cc | 148 |
5 files changed, 101 insertions, 91 deletions
diff --git a/base/event_recorder.cc b/base/event_recorder.cc index 23c092f..ddc7617 100644 --- a/base/event_recorder.cc +++ b/base/event_recorder.cc @@ -53,9 +53,6 @@ bool EventRecorder::StartRecording(std::wstring& filename) { return false; } - // Set the faster clock, if possible. - ::timeBeginPeriod(1); - // Set the recording hook. JOURNALRECORD can only be used as a global hook. journal_hook_ = ::SetWindowsHookEx(WH_JOURNALRECORD, StaticRecordWndProc, GetModuleHandle(NULL), 0); @@ -79,8 +76,6 @@ void EventRecorder::StopRecording() { return; } - ::timeEndPeriod(1); - DCHECK(file_ != NULL); fclose(file_); file_ = NULL; @@ -109,9 +104,6 @@ bool EventRecorder::StartPlayback(std::wstring& filename) { return false; } - // Set the faster clock, if possible. - ::timeBeginPeriod(1); - // Playback time is tricky. When playing back, we read a series of events, // each with timeouts. Simply subtracting the delta between two timers will // lead to fast playback (about 2x speed). The API has two events, one @@ -150,8 +142,6 @@ void EventRecorder::StopPlayback() { fclose(file_); file_ = NULL; - ::timeEndPeriod(1); - journal_hook_ = NULL; is_playing_ = false; } diff --git a/base/message_loop.cc b/base/message_loop.cc index fffeb09..ee33aa1 100644 --- a/base/message_loop.cc +++ b/base/message_loop.cc @@ -68,19 +68,6 @@ MessageLoop::MessageLoop(Type type) DCHECK(!tls_index_.Get()) << "should only have one message loop per thread"; tls_index_.Set(this); - // TODO(darin): This does not seem like the best place for this code to live! -#if defined(OS_WIN) - // We've experimented with all sorts of timers, and initially tried - // to avoid using timeBeginPeriod because it does affect the system - // globally. However, after much investigation, it turns out that all - // of the major plugins (flash, windows media 9-11, and quicktime) - // already use timeBeginPeriod to increase the speed of the clock. - // Since the browser must work with these plugins, the browser already - // needs to support a fast clock. We may as well use this ourselves, - // as it really is the best timer mechanism for our needs. - timeBeginPeriod(1); -#endif - // TODO(darin): Choose the pump based on the requested type. #if defined(OS_WIN) if (type_ == TYPE_DEFAULT) { @@ -119,11 +106,6 @@ MessageLoop::~MessageLoop() { delayed_work_queue_.pop(); delete task; } - -#if defined(OS_WIN) - // Match timeBeginPeriod() from construction. - timeEndPeriod(1); -#endif } void MessageLoop::AddDestructionObserver(DestructionObserver *obs) { diff --git a/base/time.h b/base/time.h index c25419f..cc66eda 100644 --- a/base/time.h +++ b/base/time.h @@ -436,9 +436,8 @@ class TimeTicks { int64 ticks_; #if defined(OS_WIN) - // The function to use for counting ticks. - typedef int (__stdcall *TickFunction)(void); - static TickFunction tick_function_; + typedef DWORD (*TickFunctionType)(void); + static TickFunctionType SetMockTickFunction(TickFunctionType ticker); #endif }; diff --git a/base/time_unittest_win.cc b/base/time_unittest_win.cc index 5b29db2..87cf698 100644 --- a/base/time_unittest_win.cc +++ b/base/time_unittest_win.cc @@ -12,27 +12,26 @@ namespace { class MockTimeTicks : public TimeTicks { public: - static int Ticker() { + static DWORD Ticker() { return static_cast<int>(InterlockedIncrement(&ticker_)); } static void InstallTicker() { - old_tick_function_ = tick_function_; - tick_function_ = reinterpret_cast<TickFunction>(&Ticker); + old_tick_function_ = SetMockTickFunction(&Ticker); ticker_ = -5; } static void UninstallTicker() { - tick_function_ = old_tick_function_; + SetMockTickFunction(old_tick_function_); } private: static volatile LONG ticker_; - static TickFunction old_tick_function_; + static TickFunctionType old_tick_function_; }; volatile LONG MockTimeTicks::ticker_; -MockTimeTicks::TickFunction MockTimeTicks::old_tick_function_; +MockTimeTicks::TickFunctionType MockTimeTicks::old_tick_function_; HANDLE g_rollover_test_start; diff --git a/base/time_win.cc b/base/time_win.cc index 9d38993..77c18ae 100644 --- a/base/time_win.cc +++ b/base/time_win.cc @@ -7,9 +7,11 @@ #pragma comment(lib, "winmm.lib") #include <windows.h> #include <mmsystem.h> + #include "base/basictypes.h" #include "base/lock.h" #include "base/logging.h" +#include "base/singleton.h" namespace { @@ -122,53 +124,56 @@ void Time::Explode(bool is_local, Exploded* exploded) const { } // TimeTicks ------------------------------------------------------------------ +namespace { -TimeTicks::TickFunction TimeTicks::tick_function_= - reinterpret_cast<TickFunction>(&timeGetTime); +// We define a wrapper to adapt between the __stdcall and __cdecl call of the +// mock function, and to avoid a static constructor. Assigning an import to a +// function pointer directly would require setup code to fetch from the IAT. +DWORD timeGetTimeWrapper() { + return 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<PVOID*>(&tick_lock), new_lock, NULL)) { - delete new_lock; - } +DWORD (*tick_function)(void) = &timeGetTimeWrapper; + +// 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. +class NowSingleton { + public: + NowSingleton() + : rollover_(TimeDelta::FromMilliseconds(0)), last_seen_(0) { + // Request a resolution of 1ms from timeGetTime(). This can have some + // consequences on other applications (see MSDN), but we've found that it + // is very common in other applications (Flash, Media Player, etc), so it + // is not really a dangerous thing to do. We need this because the default + // resolution of ~15ms is much too low for accurate timing and timers. + ::timeBeginPeriod(1); } - // 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; + ~NowSingleton() { + ::timeEndPeriod(1); } - // GetTickCount returns milliseconds, we want microseconds. - return TimeTicks((tick_count + rollover_count) * - Time::kMicrosecondsPerMillisecond); -} + 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 = tick_function(); + if (now < last_seen_) + rollover_ += TimeDelta::FromMilliseconds(0x100000000I64); // ~49.7 days. + last_seen_ = now; + return TimeDelta::FromMilliseconds(now) + rollover_; + } + + private: + Lock lock_; // To protected last_seen_ and rollover_. + TimeDelta rollover_; // Accumulation of time lost due 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 +203,58 @@ 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); +}; - LARGE_INTEGER now; - QueryPerformanceCounter(&now); - return TimeTicks(now.QuadPart / ticks_per_microsecond); +} // namespace + +// static +TimeTicks::TickFunctionType TimeTicks::SetMockTickFunction( + TickFunctionType ticker) { + TickFunctionType old = tick_function; + tick_function = ticker; + return old; } +// static +TimeTicks TimeTicks::Now() { + return TimeTicks() + Singleton<NowSingleton>::get()->Now(); +} + +// static +TimeTicks TimeTicks::UnreliableHighResNow() { + UnreliableHighResNowSingleton* now = + Singleton<UnreliableHighResNowSingleton>::get(); + + if (now->IsBroken()) { + NOTREACHED() << "QueryPerformanceCounter is broken."; + return TimeTicks(0); + } + + return TimeTicks() + now->Now(); +} |