Index: base/time/time_win.cc |
diff --git a/base/time/time_win.cc b/base/time/time_win.cc |
index 5fa899d1f97f69be69d1df65ba854565d7240b53..492b54a93f126717ad0b2c339e75322bdeeb1532 100644 |
--- a/base/time/time_win.cc |
+++ b/base/time/time_win.cc |
@@ -29,10 +29,7 @@ |
// |
// 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. |
+// power. |
#include "base/time/time.h" |
@@ -87,6 +84,19 @@ void InitializeClock() { |
initial_time = CurrentWallclockMicroseconds(); |
} |
+// The two values that ActivateHighResolutionTimer uses to set the systemwide |
+// timer interrupt frequency on Windows. It controls how precise timers are |
+// but also has a big impact on battery life. |
+const int kMinTimerIntervalHighResMs = 1; |
+const int kMinTimerIntervalLowResMs = 4; |
+// Track if kMinTimerIntervalHighResMs or kMinTimerIntervalLowResMs is active. |
+bool g_high_res_timer_enabled = false; |
+// How many times the high resolution timer has been called. |
+int g_high_res_timer_count = 0; |
+// The lock to control access to the above two variables. |
+base::LazyInstance<base::Lock>::Leaky g_high_res_lock = |
+ LAZY_INSTANCE_INITIALIZER; |
+ |
} // namespace |
// Time ----------------------------------------------------------------------- |
@@ -98,9 +108,6 @@ void InitializeClock() { |
// static |
const int64 Time::kTimeTToMicrosecondsOffset = GG_INT64_C(11644473600000000); |
-bool Time::high_resolution_timer_enabled_ = false; |
-int Time::high_resolution_timer_activated_ = 0; |
- |
// static |
Time Time::Now() { |
if (initial_time == 0) |
@@ -165,44 +172,51 @@ FILETIME Time::ToFileTime() const { |
// static |
void Time::EnableHighResolutionTimer(bool enable) { |
- // Test for single-threaded access. |
- static PlatformThreadId my_thread = PlatformThread::CurrentId(); |
- DCHECK(PlatformThread::CurrentId() == my_thread); |
- |
- if (high_resolution_timer_enabled_ == enable) |
+ base::AutoLock lock(g_high_res_lock.Get()); |
+ if (g_high_res_timer_enabled == enable) |
return; |
- |
- high_resolution_timer_enabled_ = enable; |
+ g_high_res_timer_enabled = enable; |
+ if (!g_high_res_timer_count) |
+ return; |
+ // Since g_high_res_timer_count != 0, an ActivateHighResolutionTimer(true) |
+ // was called which called timeBeginPeriod with g_high_res_timer_enabled |
+ // with a value which is the opposite of |enable|. With that information we |
+ // call timeEndPeriod with the same value used in timeBeginPeriod and |
+ // therefore undo the period effect. |
+ if (enable) { |
+ timeEndPeriod(kMinTimerIntervalLowResMs); |
+ timeBeginPeriod(kMinTimerIntervalHighResMs); |
+ } else { |
+ timeEndPeriod(kMinTimerIntervalHighResMs); |
+ timeBeginPeriod(kMinTimerIntervalLowResMs); |
+ } |
} |
// static |
bool Time::ActivateHighResolutionTimer(bool activating) { |
- if (!high_resolution_timer_enabled_ && activating) |
- return false; |
+ // We only do work on the transition from zero to one or one to zero so we |
+ // can easily undo the effect (if necessary) when EnableHighResolutionTimer is |
+ // called. |
+ base::AutoLock lock(g_high_res_lock.Get()); |
+ UINT period = g_high_res_timer_enabled ? kMinTimerIntervalHighResMs |
+ : kMinTimerIntervalLowResMs; |
+ int high_res_count = |
+ activating ? ++g_high_res_timer_count : --g_high_res_timer_count; |
- // Using anything other than 1ms makes timers granular |
- // to that interval. |
- const int kMinTimerIntervalMs = 1; |
- MMRESULT result; |
if (activating) { |
- result = timeBeginPeriod(kMinTimerIntervalMs); |
- high_resolution_timer_activated_++; |
+ if (high_res_count == 1) |
+ timeBeginPeriod(period); |
} else { |
- result = timeEndPeriod(kMinTimerIntervalMs); |
- high_resolution_timer_activated_--; |
+ if (high_res_count == 0) |
+ timeEndPeriod(period); |
} |
- return result == TIMERR_NOERROR; |
+ return (period == kMinTimerIntervalHighResMs); |
} |
// static |
bool Time::IsHighResolutionTimerInUse() { |
- // Note: we should track the high_resolution_timer_activated_ value |
- // under a lock if we want it to be accurate in a system with multiple |
- // message loops. We don't do that - because we don't want to take the |
- // expense of a lock for this. We *only* track this value so that unit |
- // tests can see if the high resolution timer is on or off. |
- return high_resolution_timer_enabled_ && |
- high_resolution_timer_activated_ > 0; |
+ base::AutoLock lock(g_high_res_lock.Get()); |
+ return g_high_res_timer_enabled && g_high_res_timer_count > 0; |
} |
// static |