diff options
-rw-r--r-- | app/hi_res_timer_manager_win.cc | 4 | ||||
-rw-r--r-- | base/message_loop.cc | 26 | ||||
-rw-r--r-- | base/message_loop.h | 18 | ||||
-rw-r--r-- | base/message_loop_unittest.cc | 29 | ||||
-rw-r--r-- | base/test/test_suite.h | 3 | ||||
-rw-r--r-- | base/time.h | 31 | ||||
-rw-r--r-- | base/time_win.cc | 33 | ||||
-rw-r--r-- | chrome_frame/test/net/fake_external_tab.cc | 2 |
8 files changed, 128 insertions, 18 deletions
diff --git a/app/hi_res_timer_manager_win.cc b/app/hi_res_timer_manager_win.cc index b7cee1d..6fbffca 100644 --- a/app/hi_res_timer_manager_win.cc +++ b/app/hi_res_timer_manager_win.cc @@ -25,7 +25,5 @@ void HighResolutionTimerManager::OnPowerStateChange(bool on_battery_power) { void HighResolutionTimerManager::UseHiResClock(bool use) { if (use == hi_res_clock_used_) return; - bool result = base::Time::UseHighResolutionTimer(use); - DCHECK(result); - hi_res_clock_used_ = use; + base::Time::EnableHighResolutionTimer(use); } diff --git a/base/message_loop.cc b/base/message_loop.cc index 3b13617..1668fd7 100644 --- a/base/message_loop.cc +++ b/base/message_loop.cc @@ -282,10 +282,36 @@ void MessageLoop::PostTask_Helper( if (delay_ms > 0) { pending_task.delayed_run_time = Time::Now() + TimeDelta::FromMilliseconds(delay_ms); + +#if defined(OS_WIN) + if (high_resolution_timer_expiration_.is_null()) { + // Windows timers are granular to 15.6ms. If we only set high-res + // timers for those under 15.6ms, then a 18ms timer ticks at ~32ms, + // which as a percentage is pretty inaccurate. So enable high + // res timers for any timer which is within 2x of the granularity. + // This is a tradeoff between accuracy and power management. + bool needs_high_res_timers = + delay_ms < (2 * Time::kMinLowResolutionThresholdMs); + if (needs_high_res_timers) { + Time::ActivateHighResolutionTimer(true); + high_resolution_timer_expiration_ = base::TimeTicks::Now() + + TimeDelta::FromMilliseconds(kHighResolutionTimerModeLeaseTimeMs); + } + } +#endif } else { DCHECK_EQ(delay_ms, 0) << "delay should not be negative"; } +#if defined(OS_WIN) + if (!high_resolution_timer_expiration_.is_null()) { + if (base::TimeTicks::Now() > high_resolution_timer_expiration_) { + Time::ActivateHighResolutionTimer(false); + high_resolution_timer_expiration_ = base::TimeTicks(); + } + } +#endif + // Warning: Don't try to short-circuit, and handle this thread's tasks more // directly, as it could starve handling of foreign threads. Put every task // into this queue. diff --git a/base/message_loop.h b/base/message_loop.h index 8b4be7c..8deacec 100644 --- a/base/message_loop.h +++ b/base/message_loop.h @@ -287,6 +287,20 @@ class MessageLoop : public base::MessagePump::Delegate { typedef base::MessagePumpForUI::Observer Observer; #endif + // Returns true if the message loop has high resolution timers enabled. + // Provided for testing. + bool high_resolution_timers_enabled() { +#if defined(OS_WIN) + return !high_resolution_timer_expiration_.is_null(); +#else + return true; +#endif + } + + // When we go into high resolution timer mode, we will stay in hi-res mode + // for at least 1s. + static const int kHighResolutionTimerModeLeaseTimeMs = 1000; + //---------------------------------------------------------------------------- protected: struct RunState { @@ -454,6 +468,10 @@ class MessageLoop : public base::MessagePump::Delegate { RunState* state_; +#if defined(OS_WIN) + base::TimeTicks high_resolution_timer_expiration_; +#endif + // The next sequence number to use for delayed tasks. int next_sequence_num_; diff --git a/base/message_loop_unittest.cc b/base/message_loop_unittest.cc index da0013c..719a01a 100644 --- a/base/message_loop_unittest.cc +++ b/base/message_loop_unittest.cc @@ -1539,6 +1539,35 @@ TEST(MessageLoopTest, IOHandler) { TEST(MessageLoopTest, WaitForIO) { RunTest_WaitForIO(); } + +TEST(MessageLoopTest, HighResolutionTimer) { + MessageLoop loop; + + const int kFastTimerMs = 5; + const int kSlowTimerMs = 100; + + EXPECT_EQ(false, loop.high_resolution_timers_enabled()); + + // Post a fast task to enable the high resolution timers. + loop.PostDelayedTask(FROM_HERE, new DummyTask(1), kFastTimerMs); + loop.Run(); + EXPECT_EQ(true, loop.high_resolution_timers_enabled()); + + // Post a slow task and verify high resolution timers + // are still enabled. + loop.PostDelayedTask(FROM_HERE, new DummyTask(1), kSlowTimerMs); + loop.Run(); + EXPECT_EQ(true, loop.high_resolution_timers_enabled()); + + // Wait for a while so that high-resolution mode elapses. + Sleep(MessageLoop::kHighResolutionTimerModeLeaseTimeMs); + + // Post a slow task to disable the high resolution timers. + loop.PostDelayedTask(FROM_HERE, new DummyTask(1), kSlowTimerMs); + loop.Run(); + EXPECT_EQ(false, loop.high_resolution_timers_enabled()); +} + #endif // defined(OS_WIN) #if defined(OS_POSIX) diff --git a/base/test/test_suite.h b/base/test/test_suite.h index c026709..ff6f131 100644 --- a/base/test/test_suite.h +++ b/base/test/test_suite.h @@ -229,8 +229,7 @@ class TestSuite { #if defined(OS_WIN) // Make sure we run with high resolution timer to minimize differences // between production code and test code. - bool result = base::Time::UseHighResolutionTimer(true); - CHECK(result); + base::Time::EnableHighResolutionTimer(true); #endif // defined(OS_WIN) // In some cases, we do not want to see standard error dialogs. diff --git a/base/time.h b/base/time.h index 6e9a8a4..98a711d 100644 --- a/base/time.h +++ b/base/time.h @@ -254,9 +254,27 @@ class Time { static Time FromFileTime(FILETIME ft); FILETIME ToFileTime() const; - // Enable or disable Windows high resolution timer. For more details - // see comments in time_win.cc. Returns true on success. - static bool UseHighResolutionTimer(bool use); + // The minimum time of a low resolution timer. This is basically a windows + // constant of ~15.6ms. While it does vary on some older OS versions, we'll + // treat it as static across all windows versions. + static const int kMinLowResolutionThresholdMs = 16; + + // Enable or disable Windows high resolution timer. If the high resolution + // timer is not enabled, calls to ActivateHighResolutionTimer will fail. + // When disabling the high resolution timer, this function will not cause + // the high resolution timer to be deactivated, but will prevent future + // activations. + // Must be called from the main thread. + // For more details see comments in time_win.cc. + static void EnableHighResolutionTimer(bool enable); + + // Activates or deactivates the high resolution timer based on the |activate| + // flag. If the HighResolutionTimer is not Enabled (see + // EnableHighResolutionTimer), this function will return false. Otherwise + // returns true. + // All callers to activate the high resolution timer must eventually call + // this function to deactivate the high resolution timer. + static bool ActivateHighResolutionTimer(bool activate); #endif // Converts an exploded structure representing either the local time or UTC @@ -370,6 +388,13 @@ class Time { // platform-dependent epoch. static const int64 kTimeTToMicrosecondsOffset; +#if defined(OS_WIN) + // Indicates whether fast timers are usable right now. For instance, + // when using battery power, we might elect to prevent high speed timers + // which would draw more power. + static bool high_resolution_timer_enabled_; +#endif + // Time in microseconds in UTC. int64 us_; }; diff --git a/base/time_win.cc b/base/time_win.cc index f44c7f1..d4da0f4 100644 --- a/base/time_win.cc +++ b/base/time_win.cc @@ -98,6 +98,8 @@ void InitializeClock() { // static const int64 Time::kTimeTToMicrosecondsOffset = GG_INT64_C(11644473600000000); +bool Time::high_resolution_timer_enabled_ = false; + // static Time Time::Now() { if (initial_time == 0) @@ -148,18 +150,31 @@ FILETIME Time::ToFileTime() const { } // static -bool Time::UseHighResolutionTimer(bool use) { - // TODO(mbelshe): Make sure that switching the system timer resolution - // doesn't break Timer firing order etc. An example test would be to have - // two threads. One would have a bunch of timers, and another would turn the - // high resolution timer on and off. +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) + return; + + high_resolution_timer_enabled_ = enable; +} + +// static +bool Time::ActivateHighResolutionTimer(bool activate) { + if (!high_resolution_timer_enabled_) + return false; + // Using anything other than 1ms makes timers granular + // to that interval. + const int kMinTimerIntervalMs = 1; MMRESULT result; - if (use) - result = timeBeginPeriod(1); + if (activate) + result = timeBeginPeriod(kMinTimerIntervalMs); else - result = timeEndPeriod(1); - return (result == TIMERR_NOERROR); + result = timeEndPeriod(kMinTimerIntervalMs); + return result == TIMERR_NOERROR; } // static diff --git a/chrome_frame/test/net/fake_external_tab.cc b/chrome_frame/test/net/fake_external_tab.cc index 7c84ab7..aab6e63 100644 --- a/chrome_frame/test/net/fake_external_tab.cc +++ b/chrome_frame/test/net/fake_external_tab.cc @@ -304,7 +304,7 @@ void CFUrlRequestUnittestRunner::Initialize() { // directly because it will attempt to initialize some things such as // ICU that have already been initialized for this process. InitializeLogging(); - base::Time::UseHighResolutionTimer(true); + base::Time::EnableHighResolutionTimer(true); SuppressErrorDialogs(); DebugUtil::SuppressDialogs(); |