summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--app/hi_res_timer_manager_win.cc4
-rw-r--r--base/message_loop.cc26
-rw-r--r--base/message_loop.h18
-rw-r--r--base/message_loop_unittest.cc29
-rw-r--r--base/test/test_suite.h3
-rw-r--r--base/time.h31
-rw-r--r--base/time_win.cc33
-rw-r--r--chrome_frame/test/net/fake_external_tab.cc2
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();