| Index: base/time_win.cc
|
| ===================================================================
|
| --- base/time_win.cc (revision 2585)
|
| +++ base/time_win.cc (working copy)
|
| @@ -2,6 +2,38 @@
|
| // Use of this source code is governed by a BSD-style license that can be
|
| // found in the LICENSE file.
|
|
|
| +
|
| +// Windows Timer Primer
|
| +//
|
| +// A good article: http://www.ddj.com/windows/184416651
|
| +// A good mozilla bug: http://bugzilla.mozilla.org/show_bug.cgi?id=363258
|
| +//
|
| +// The default windows timer, GetSystemTimeAsFileTime is not very precise.
|
| +// It is only good to ~15.5ms.
|
| +//
|
| +// QueryPerformanceCounter is the logical choice for a high-precision timer.
|
| +// However, it is known to be buggy on some hardware. Specifically, it can
|
| +// sometimes "jump". On laptops, QPC can also be very expensive to call.
|
| +// It's 3-4x slower than timeGetTime() on desktops, but can be 10x slower
|
| +// on laptops. A unittest exists which will show the relative cost of various
|
| +// timers on any system.
|
| +//
|
| +// The next logical choice is timeGetTime(). timeGetTime has a precision of
|
| +// 1ms, but only if you call APIs (timeBeginPeriod()) which affect all other
|
| +// applications on the system. By default, precision is only 15.5ms.
|
| +// Unfortunately, we don't want to call timeBeginPeriod because we don't
|
| +// want to affect other applications. Further, on mobile platforms, use of
|
| +// faster multimedia timers can hurt battery life. See the intel
|
| +// article about this here:
|
| +// http://softwarecommunity.intel.com/articles/eng/1086.htm
|
| +//
|
| +// To work around all this, we're going to generally use timeGetTime(). We
|
| +// will only increase the system-wide timer if we're not running on battery
|
| +// power. Using timeBeginPeriod(1) is a requirement in order to make our
|
| +// message loop waits have the same resolution that our time measurements
|
| +// do. Otherwise, WaitForSingleObject(..., 1) will no less than 15ms when
|
| +// there is nothing else to waken the Wait.
|
| +
|
| #include "base/time.h"
|
|
|
| #pragma comment(lib, "winmm.lib")
|
| @@ -11,7 +43,9 @@
|
| #include "base/basictypes.h"
|
| #include "base/lock.h"
|
| #include "base/logging.h"
|
| +#include "base/cpu.h"
|
| #include "base/singleton.h"
|
| +#include "base/system_monitor.h"
|
|
|
| namespace {
|
|
|
| @@ -174,6 +208,7 @@
|
| return timeGetTime();
|
| }
|
|
|
| +
|
| DWORD (*tick_function)(void) = &timeGetTimeWrapper;
|
|
|
| // We use timeGetTime() to implement TimeTicks::Now(). This can be problematic
|
| @@ -181,20 +216,20 @@
|
| // 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 {
|
| +class NowSingleton : public base::SystemMonitor::PowerObserver {
|
| 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);
|
| + : rollover_(TimeDelta::FromMilliseconds(0)),
|
| + last_seen_(0),
|
| + hi_res_clock_enabled_(false) {
|
| + base::SystemMonitor* system = base::SystemMonitor::Get();
|
| + system->AddObserver(this);
|
| + UseHiResClock(!system->BatteryPower());
|
| }
|
|
|
| ~NowSingleton() {
|
| - ::timeEndPeriod(1);
|
| + UseHiResClock(false);
|
| + base::SystemMonitor::Get()->RemoveObserver(this);
|
| }
|
|
|
| TimeDelta Now() {
|
| @@ -208,10 +243,30 @@
|
| return TimeDelta::FromMilliseconds(now) + rollover_;
|
| }
|
|
|
| + // Interfaces for monitoring Power changes.
|
| + void OnPowerStateChange(base::SystemMonitor* system) {
|
| + UseHiResClock(!system->BatteryPower());
|
| + }
|
| +
|
| + void OnSuspend(base::SystemMonitor* system) {}
|
| + void OnResume(base::SystemMonitor* system) {}
|
| +
|
| private:
|
| + // Enable or disable the faster multimedia timer.
|
| + void UseHiResClock(bool enabled) {
|
| + if (enabled == hi_res_clock_enabled_)
|
| + return;
|
| + if (enabled)
|
| + timeBeginPeriod(1);
|
| + else
|
| + timeEndPeriod(1);
|
| + hi_res_clock_enabled_ = enabled;
|
| + }
|
| +
|
| 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.
|
| + bool hi_res_clock_enabled_;
|
|
|
| DISALLOW_COPY_AND_ASSIGN(NowSingleton);
|
| };
|
| @@ -244,32 +299,75 @@
|
| // (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 {
|
| +class HighResNowSingleton {
|
| public:
|
| - UnreliableHighResNowSingleton() : ticks_per_microsecond_(0) {
|
| + HighResNowSingleton()
|
| + : ticks_per_microsecond_(0.0),
|
| + skew_(0) {
|
| + InitializeClock();
|
| +
|
| + // On Athlon X2 CPUs (e.g. model 15) QueryPerformanceCounter is
|
| + // unreliable. Fallback to low-res clock.
|
| + base::CPU cpu;
|
| + if (cpu.vendor_name() == "AuthenticAMD" && cpu.family() == 15)
|
| + DisableHighResClock();
|
| + }
|
| +
|
| + bool IsUsingHighResClock() {
|
| + return ticks_per_microsecond_ != 0.0;
|
| + }
|
| +
|
| + void DisableHighResClock() {
|
| + ticks_per_microsecond_ = 0.0;
|
| + }
|
| +
|
| + TimeDelta Now() {
|
| + // Our maximum tolerance for QPC drifting.
|
| + const int kMaxTimeDrift = 50 * Time::kMicrosecondsPerMillisecond;
|
| +
|
| + if (IsUsingHighResClock()) {
|
| + int64 now = UnreliableNow();
|
| +
|
| + // Verify that QPC does not seem to drift.
|
| + DCHECK(now - ReliableNow() - skew_ < kMaxTimeDrift);
|
| +
|
| + return TimeDelta::FromMicroseconds(now);
|
| + }
|
| +
|
| + // Just fallback to the slower clock.
|
| + return Singleton<NowSingleton>::get()->Now();
|
| + }
|
| +
|
| + private:
|
| + // Synchronize the QPC clock with GetSystemTimeAsFileTime.
|
| + void InitializeClock() {
|
| 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;
|
| - }
|
| + ticks_per_microsecond_ = static_cast<float>(ticks_per_sec.QuadPart) /
|
| + static_cast<float>(Time::kMicrosecondsPerSecond);
|
|
|
| - bool IsBroken() {
|
| - return ticks_per_microsecond_ == 0;
|
| + skew_ = UnreliableNow() - ReliableNow();
|
| }
|
|
|
| - TimeDelta Now() {
|
| + // Get the number of microseconds since boot in a reliable fashion
|
| + int64 UnreliableNow() {
|
| LARGE_INTEGER now;
|
| QueryPerformanceCounter(&now);
|
| - return TimeDelta::FromMicroseconds(now.QuadPart / ticks_per_microsecond_);
|
| + return static_cast<int64>(now.QuadPart / ticks_per_microsecond_);
|
| }
|
|
|
| - private:
|
| + // Get the number of microseconds since boot in a reliable fashion
|
| + int64 ReliableNow() {
|
| + return Singleton<NowSingleton>::get()->Now().InMicroseconds();
|
| + }
|
| +
|
| // Cached clock frequency -> microseconds. This assumes that the clock
|
| // frequency is faster than one microsecond (which is 1MHz, should be OK).
|
| - int64 ticks_per_microsecond_; // 0 indicates QPF failed and we're broken.
|
| + float ticks_per_microsecond_; // 0 indicates QPF failed and we're broken.
|
| + int64 skew_; // Skew between lo-res and hi-res clocks (for debugging).
|
|
|
| - DISALLOW_COPY_AND_ASSIGN(UnreliableHighResNowSingleton);
|
| + DISALLOW_COPY_AND_ASSIGN(HighResNowSingleton);
|
| };
|
|
|
| } // namespace
|
| @@ -288,14 +386,6 @@
|
| }
|
|
|
| // static
|
| -TimeTicks TimeTicks::UnreliableHighResNow() {
|
| - UnreliableHighResNowSingleton* now =
|
| - Singleton<UnreliableHighResNowSingleton>::get();
|
| -
|
| - if (now->IsBroken()) {
|
| - NOTREACHED() << "QueryPerformanceCounter is broken.";
|
| - return TimeTicks(0);
|
| - }
|
| -
|
| - return TimeTicks() + now->Now();
|
| +TimeTicks TimeTicks::HighResNow() {
|
| + return TimeTicks() + Singleton<HighResNowSingleton>::get()->Now();
|
| }
|
|
|