This is jamesr@ code I am landing.
On Windows the message pump code tried to manage the systemwide timer resolution to fire delayed tasks with better than 15ms resolution but it was buggy:
1- A short task that was not followed by any other task will leave the systemwide timer pegged to 1ms
2- After we decided to crank up the timer we would 'lease' the timer for 1 second, for no good reason.
Both issues are detrimental to battery power.
The source of both problems is that we tried to decide with incomplete information. This patch solves that by having 1 bit for each pending task that requires a high resolution timer and a sum of the number of tasks that require high res timers.
BUG=153139
TEST=included here, also see the bug for manual testing.
Review URL: https://codereview.chromium.org/395913006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@284625 0039d316-1c4b-4281-b951-d872f2087c98
CrOS-Libchrome-Original-Commit: f9855fb1637fbca5d0164ae602b98b6fe460e74b
diff --git a/base/message_loop/message_loop.cc b/base/message_loop/message_loop.cc
index a7217eb..2d05ef1 100644
--- a/base/message_loop/message_loop.cc
+++ b/base/message_loop/message_loop.cc
@@ -127,6 +127,8 @@
MessageLoop::MessageLoop(Type type)
: type_(type),
+ pending_high_res_tasks_(0),
+ in_high_res_mode_(false),
nestable_tasks_allowed_(true),
#if defined(OS_WIN)
os_modal_loop_(false),
@@ -141,6 +143,8 @@
MessageLoop::MessageLoop(scoped_ptr<MessagePump> pump)
: pump_(pump.Pass()),
type_(TYPE_CUSTOM),
+ pending_high_res_tasks_(0),
+ in_high_res_mode_(false),
nestable_tasks_allowed_(true),
#if defined(OS_WIN)
os_modal_loop_(false),
@@ -153,8 +157,11 @@
MessageLoop::~MessageLoop() {
DCHECK_EQ(this, current());
-
DCHECK(!run_loop_);
+#if defined(OS_WIN)
+ if (in_high_res_mode_)
+ Time::ActivateHighResolutionTimer(false);
+#endif
// Clean up any unprocessed tasks, but take care: deleting a task could
// result in the addition of more tasks (e.g., via DeleteSoon). We set a
@@ -369,8 +376,8 @@
return run_loop_ != NULL;
}
-bool MessageLoop::IsHighResolutionTimerEnabledForTesting() {
- return incoming_task_queue_->IsHighResolutionTimerEnabledForTesting();
+bool MessageLoop::HasHighResolutionTasks() {
+ return incoming_task_queue_->HasHighResolutionTasks();
}
bool MessageLoop::IsIdleForTesting() {
@@ -438,6 +445,11 @@
"src_file", pending_task.posted_from.file_name(),
"src_func", pending_task.posted_from.function_name());
+ if (pending_task.is_high_res) {
+ pending_high_res_tasks_--;
+ CHECK(pending_high_res_tasks_ >= 0);
+ }
+
DCHECK(nestable_tasks_allowed_);
// Execute the task and assume the worst: It is probably not reentrant.
nestable_tasks_allowed_ = false;
@@ -523,8 +535,10 @@
// |*work_queue| by waiting until the last minute (|*work_queue| is empty) to
// load. That reduces the number of locks-per-task significantly when our
// queues get large.
- if (work_queue_.empty())
- incoming_task_queue_->ReloadWorkQueue(&work_queue_);
+ if (work_queue_.empty()) {
+ pending_high_res_tasks_ +=
+ incoming_task_queue_->ReloadWorkQueue(&work_queue_);
+ }
}
void MessageLoop::ScheduleWork(bool was_empty) {
@@ -629,6 +643,14 @@
if (run_loop_->quit_when_idle_received_)
pump_->Quit();
+ // We will now do a kernel wait for more tasks.
+#if defined(OS_WIN)
+ // The Windows scheduler has by default ~15ms resolution. If we have high
+ // resolution tasks pending we need to temporarity increase the systemwide
+ // timer resolution to 1ms, and if we don't we need to go back to 15ms.
+ in_high_res_mode_ = pending_high_res_tasks_ > 0;
+ Time::ActivateHighResolutionTimer(in_high_res_mode_);
+#endif
return false;
}