| Index: base/time_win.cc
|
| ===================================================================
|
| --- base/time_win.cc (revision 1835)
|
| +++ base/time_win.cc (working copy)
|
| @@ -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,54 +124,50 @@
|
| }
|
|
|
| // TimeTicks ------------------------------------------------------------------
|
| +namespace {
|
|
|
| -TimeTicks::TickFunction TimeTicks::tick_function_=
|
| - reinterpret_cast<TickFunction>(&timeGetTime);
|
| +DWORD (__stdcall *tick_function)(void) = &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.
|
| +// 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);
|
| + }
|
|
|
| - // 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.
|
| + ~NowSingleton() {
|
| + ::timeEndPeriod(1);
|
| + }
|
|
|
| - // 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;
|
| - }
|
| + 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_;
|
| }
|
|
|
| - // 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);
|
| + 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.
|
|
|
| - last_tick_count = tick_count;
|
| - rollover_count = tick_rollover_accum;
|
| - }
|
| + DISALLOW_COPY_AND_ASSIGN(NowSingleton);
|
| +};
|
|
|
| - // GetTickCount returns milliseconds, we want microseconds.
|
| - return TimeTicks((tick_count + rollover_count) *
|
| - Time::kMicrosecondsPerMillisecond);
|
| -}
|
| -
|
| // Overview of time counters:
|
| // (1) CPU cycle counter. (Retrieved via RDTSC)
|
| // The CPU counter provides the highest resolution time stamp and is the least
|
| @@ -198,23 +196,58 @@
|
| // (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
|
| +
|
| +// 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);
|
| }
|
|
|
| - LARGE_INTEGER now;
|
| - QueryPerformanceCounter(&now);
|
| - return TimeTicks(now.QuadPart / ticks_per_microsecond);
|
| + return TimeTicks() + now->Now();
|
| }
|
| -
|
|
|