diff options
author | jamesr <jamesr@chromium.org> | 2014-12-12 17:55:30 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-12-13 01:56:02 +0000 |
commit | 23a5e54a3ae47b3f5dfb9ca62e19f10db02d1b42 (patch) | |
tree | a0b99a2e245e761bd170df35abda4e456fcd248b /base | |
parent | 0bc1835a0643e009c8d5402056eaf0e1c341864f (diff) | |
download | chromium_src-23a5e54a3ae47b3f5dfb9ca62e19f10db02d1b42.zip chromium_src-23a5e54a3ae47b3f5dfb9ca62e19f10db02d1b42.tar.gz chromium_src-23a5e54a3ae47b3f5dfb9ca62e19f10db02d1b42.tar.bz2 |
Avoid scheduling a message loop that we know is not sleeping
When tasks are posted to a message loop, we may need to schedule the
message loop to wake up if it was sleeping so it processes the message.
The incoming task queue checks whether the incoming queue was already
empty as a way to avoid scheduling unnecessarily, but we can be more
precise. Once a message loop is scheduled, we know that it will sooner
or later wake up and enter its run loop. In its run loop it will
repeatedly drain the work queue and attempt to reload from the incoming
queue until there is no work immediately available to do. This means that
before waiting for more work the loop will always reload from an empty
incoming queue. Thus, once we schedule a loop we do not need to schedule
the same loop again until after an empty reload occurs.
This adds a bool to IncomingTaskQueue protected by the incoming task
queue lock that keeps track of if the loop has been scheduled since the
last empty reload.
Local testing on Linux desktop and a ChromeShell Android build shows
that this avoids roughly 40% of the ScheduleWork calls when browsing
around to random websites.
This also has the very nice consequence that when running a task and
posting another task to the same thread we *never* need to invoke
ScheduleWork which avoids the cost of the ScheduleWork call and avoids
a spurious wakeup when going to sleep.
BUG=412137
Review URL: https://codereview.chromium.org/614723003
Cr-Commit-Position: refs/heads/master@{#308221}
Diffstat (limited to 'base')
-rw-r--r-- | base/message_loop/incoming_task_queue.cc | 43 | ||||
-rw-r--r-- | base/message_loop/incoming_task_queue.h | 8 | ||||
-rw-r--r-- | base/message_loop/message_loop.cc | 17 | ||||
-rw-r--r-- | base/message_loop/message_loop.h | 2 | ||||
-rw-r--r-- | base/message_loop/message_pump_perftest.cc | 2 |
5 files changed, 50 insertions, 22 deletions
diff --git a/base/message_loop/incoming_task_queue.cc b/base/message_loop/incoming_task_queue.cc index 2ca32d6..9e5b013 100644 --- a/base/message_loop/incoming_task_queue.cc +++ b/base/message_loop/incoming_task_queue.cc @@ -6,16 +6,36 @@ #include "base/location.h" #include "base/message_loop/message_loop.h" +#include "base/metrics/histogram.h" #include "base/synchronization/waitable_event.h" #include "base/time/time.h" namespace base { namespace internal { +namespace { + +// Returns true if MessagePump::ScheduleWork() must be called one +// time for every task that is added to the MessageLoop incoming queue. +bool AlwaysNotifyPump(MessageLoop::Type type) { +#if defined(OS_ANDROID) + // The Android UI message loop needs to get notified each time a task is + // added + // to the incoming queue. + return type == MessageLoop::TYPE_UI || type == MessageLoop::TYPE_JAVA; +#else + return false; +#endif +} + +} // namespace + IncomingTaskQueue::IncomingTaskQueue(MessageLoop* message_loop) : high_res_task_count_(0), message_loop_(message_loop), - next_sequence_num_(0) { + next_sequence_num_(0), + message_loop_scheduled_(false), + always_schedule_work_(AlwaysNotifyPump(message_loop_->type())) { } bool IncomingTaskQueue::AddToIncomingQueue( @@ -56,9 +76,14 @@ int IncomingTaskQueue::ReloadWorkQueue(TaskQueue* work_queue) { // Acquire all we can from the inter-thread queue with one lock acquisition. AutoLock lock(incoming_queue_lock_); - if (!incoming_queue_.empty()) + if (incoming_queue_.empty()) { + // If the loop attempts to reload but there are no tasks in the incoming + // queue, that means it will go to sleep waiting for more work. If the + // incoming queue becomes nonempty we need to schedule it again. + message_loop_scheduled_ = false; + } else { incoming_queue_.Swap(work_queue); - + } // Reset the count of high resolution tasks since our queue is now empty. int high_res_tasks = high_res_task_count_; high_res_task_count_ = 0; @@ -109,8 +134,16 @@ bool IncomingTaskQueue::PostPendingTask(PendingTask* pending_task) { incoming_queue_.push(*pending_task); pending_task->task.Reset(); - // Wake up the pump. - message_loop_->ScheduleWork(was_empty); + if (always_schedule_work_ || (!message_loop_scheduled_ && was_empty)) { + // Wake up the message loop. + message_loop_->ScheduleWork(); + // After we've scheduled the message loop, we do not need to do so again + // until we know it has processed all of the work in our queue and is + // waiting for more work again. The message loop will always attempt to + // reload from the incoming queue before waiting again so we clear this flag + // in ReloadWorkQueue(). + message_loop_scheduled_ = true; + } return true; } diff --git a/base/message_loop/incoming_task_queue.h b/base/message_loop/incoming_task_queue.h index 30f2603..72e1f30 100644 --- a/base/message_loop/incoming_task_queue.h +++ b/base/message_loop/incoming_task_queue.h @@ -84,6 +84,14 @@ class BASE_EXPORT IncomingTaskQueue // The next sequence number to use for delayed tasks. int next_sequence_num_; + // True if our message loop has already been scheduled and does not need to be + // scheduled again until an empty reload occurs. + bool message_loop_scheduled_; + + // True if we always need to call ScheduleWork when receiving a new task, even + // if the incoming queue was not empty. + const bool always_schedule_work_; + DISALLOW_COPY_AND_ASSIGN(IncomingTaskQueue); }; diff --git a/base/message_loop/message_loop.cc b/base/message_loop/message_loop.cc index 2f4a03c..8180733 100644 --- a/base/message_loop/message_loop.cc +++ b/base/message_loop/message_loop.cc @@ -86,18 +86,6 @@ bool enable_histogrammer_ = false; MessageLoop::MessagePumpFactory* message_pump_for_ui_factory_ = NULL; -// Returns true if MessagePump::ScheduleWork() must be called one -// time for every task that is added to the MessageLoop incoming queue. -bool AlwaysNotifyPump(MessageLoop::Type type) { -#if defined(OS_ANDROID) - // The Android UI message loop needs to get notified each time a task is added - // to the incoming queue. - return type == MessageLoop::TYPE_UI || type == MessageLoop::TYPE_JAVA; -#else - return false; -#endif -} - #if defined(OS_IOS) typedef MessagePumpIOSForIO MessagePumpForIO; #elif defined(OS_NACL_SFI) @@ -512,9 +500,8 @@ void MessageLoop::ReloadWorkQueue() { } } -void MessageLoop::ScheduleWork(bool was_empty) { - if (was_empty || AlwaysNotifyPump(type_)) - pump_->ScheduleWork(); +void MessageLoop::ScheduleWork() { + pump_->ScheduleWork(); } //------------------------------------------------------------------------------ diff --git a/base/message_loop/message_loop.h b/base/message_loop/message_loop.h index 47df69a..32e826d 100644 --- a/base/message_loop/message_loop.h +++ b/base/message_loop/message_loop.h @@ -391,7 +391,7 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate { // Wakes up the message pump. Can be called on any thread. The caller is // responsible for synchronizing ScheduleWork() calls. - void ScheduleWork(bool was_empty); + void ScheduleWork(); // Returns the TaskAnnotator which is used to add debug information to posted // tasks. diff --git a/base/message_loop/message_pump_perftest.cc b/base/message_loop/message_pump_perftest.cc index 9fca465..39ed5a8 100644 --- a/base/message_loop/message_pump_perftest.cc +++ b/base/message_loop/message_pump_perftest.cc @@ -39,7 +39,7 @@ class ScheduleWorkTest : public testing::Test { uint64_t schedule_calls = 0u; do { for (size_t i = 0; i < kBatchSize; ++i) { - target_message_loop()->ScheduleWork(true); + target_message_loop()->ScheduleWork(); schedule_calls++; } now = base::TimeTicks::HighResNow(); |