summaryrefslogtreecommitdiffstats
path: root/base
diff options
context:
space:
mode:
authorjamesr <jamesr@chromium.org>2014-12-12 17:55:30 -0800
committerCommit bot <commit-bot@chromium.org>2014-12-13 01:56:02 +0000
commit23a5e54a3ae47b3f5dfb9ca62e19f10db02d1b42 (patch)
treea0b99a2e245e761bd170df35abda4e456fcd248b /base
parent0bc1835a0643e009c8d5402056eaf0e1c341864f (diff)
downloadchromium_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.cc43
-rw-r--r--base/message_loop/incoming_task_queue.h8
-rw-r--r--base/message_loop/message_loop.cc17
-rw-r--r--base/message_loop/message_loop.h2
-rw-r--r--base/message_loop/message_pump_perftest.cc2
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();