|  | // Copyright 2013 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "base/message_loop/incoming_task_queue.h" | 
|  |  | 
|  | #include <limits> | 
|  | #include <utility> | 
|  |  | 
|  | #include "base/location.h" | 
|  | #include "base/message_loop/message_loop.h" | 
|  | #include "base/synchronization/waitable_event.h" | 
|  | #include "base/time/time.h" | 
|  | #include "build_config.h" | 
|  |  | 
|  | namespace base { | 
|  | namespace internal { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | #if DCHECK_IS_ON() | 
|  | // Delays larger than this are often bogus, and a warning should be emitted in | 
|  | // debug builds to warn developers.  http://crbug.com/450045 | 
|  | constexpr TimeDelta kTaskDelayWarningThreshold = TimeDelta::FromDays(14); | 
|  | #endif | 
|  |  | 
|  | // 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 | 
|  | } | 
|  |  | 
|  | TimeTicks CalculateDelayedRuntime(TimeDelta delay) { | 
|  | TimeTicks delayed_run_time; | 
|  | if (delay > TimeDelta()) | 
|  | delayed_run_time = TimeTicks::Now() + delay; | 
|  | else | 
|  | DCHECK_EQ(delay.InMilliseconds(), 0) << "delay should not be negative"; | 
|  | return delayed_run_time; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | IncomingTaskQueue::IncomingTaskQueue(MessageLoop* message_loop) | 
|  | : always_schedule_work_(AlwaysNotifyPump(message_loop->type())), | 
|  | triage_tasks_(this), | 
|  | delayed_tasks_(this), | 
|  | deferred_tasks_(this), | 
|  | message_loop_(message_loop) { | 
|  | // The constructing sequence is not necessarily the running sequence in the | 
|  | // case of base::Thread. | 
|  | DETACH_FROM_SEQUENCE(sequence_checker_); | 
|  | } | 
|  |  | 
|  | bool IncomingTaskQueue::AddToIncomingQueue(const Location& from_here, | 
|  | OnceClosure task, | 
|  | TimeDelta delay, | 
|  | Nestable nestable) { | 
|  | // Use CHECK instead of DCHECK to crash earlier. See http://crbug.com/711167 | 
|  | // for details. | 
|  | CHECK(task); | 
|  | DLOG_IF(WARNING, delay > kTaskDelayWarningThreshold) | 
|  | << "Requesting super-long task delay period of " << delay.InSeconds() | 
|  | << " seconds from here: " << from_here.ToString(); | 
|  |  | 
|  | PendingTask pending_task(from_here, std::move(task), | 
|  | CalculateDelayedRuntime(delay), nestable); | 
|  | #if defined(OS_WIN) | 
|  | // We consider the task needs a high resolution timer if the delay is | 
|  | // more than 0 and less than 32ms. This caps the relative error to | 
|  | // less than 50% : a 33ms wait can wake at 48ms since the default | 
|  | // resolution on Windows is between 10 and 15ms. | 
|  | if (delay > TimeDelta() && | 
|  | delay.InMilliseconds() < (2 * Time::kMinLowResolutionThresholdMs)) { | 
|  | pending_task.is_high_res = true; | 
|  | } | 
|  | #endif | 
|  | return PostPendingTask(&pending_task); | 
|  | } | 
|  |  | 
|  | void IncomingTaskQueue::WillDestroyCurrentMessageLoop() { | 
|  | { | 
|  | AutoLock auto_lock(incoming_queue_lock_); | 
|  | accept_new_tasks_ = false; | 
|  | } | 
|  | { | 
|  | AutoLock auto_lock(message_loop_lock_); | 
|  | message_loop_ = nullptr; | 
|  | } | 
|  | } | 
|  |  | 
|  | void IncomingTaskQueue::StartScheduling() { | 
|  | bool schedule_work; | 
|  | { | 
|  | AutoLock lock(incoming_queue_lock_); | 
|  | DCHECK(!is_ready_for_scheduling_); | 
|  | DCHECK(!message_loop_scheduled_); | 
|  | is_ready_for_scheduling_ = true; | 
|  | schedule_work = !incoming_queue_.empty(); | 
|  | if (schedule_work) | 
|  | message_loop_scheduled_ = true; | 
|  | } | 
|  | if (schedule_work) { | 
|  | DCHECK(message_loop_); | 
|  | AutoLock auto_lock(message_loop_lock_); | 
|  | message_loop_->ScheduleWork(); | 
|  | } | 
|  | } | 
|  |  | 
|  | IncomingTaskQueue::~IncomingTaskQueue() { | 
|  | // Verify that WillDestroyCurrentMessageLoop() has been called. | 
|  | DCHECK(!message_loop_); | 
|  | } | 
|  |  | 
|  | void IncomingTaskQueue::RunTask(PendingTask* pending_task) { | 
|  | DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
|  | task_annotator_.RunTask("MessageLoop::PostTask", pending_task); | 
|  | } | 
|  |  | 
|  | IncomingTaskQueue::TriageQueue::TriageQueue(IncomingTaskQueue* outer) | 
|  | : outer_(outer) {} | 
|  |  | 
|  | IncomingTaskQueue::TriageQueue::~TriageQueue() = default; | 
|  |  | 
|  | const PendingTask& IncomingTaskQueue::TriageQueue::Peek() { | 
|  | DCHECK_CALLED_ON_VALID_SEQUENCE(outer_->sequence_checker_); | 
|  | ReloadFromIncomingQueueIfEmpty(); | 
|  | DCHECK(!queue_.empty()); | 
|  | return queue_.front(); | 
|  | } | 
|  |  | 
|  | PendingTask IncomingTaskQueue::TriageQueue::Pop() { | 
|  | DCHECK_CALLED_ON_VALID_SEQUENCE(outer_->sequence_checker_); | 
|  | ReloadFromIncomingQueueIfEmpty(); | 
|  | DCHECK(!queue_.empty()); | 
|  | PendingTask pending_task = std::move(queue_.front()); | 
|  | queue_.pop(); | 
|  |  | 
|  | if (pending_task.is_high_res) | 
|  | --outer_->pending_high_res_tasks_; | 
|  |  | 
|  | return pending_task; | 
|  | } | 
|  |  | 
|  | bool IncomingTaskQueue::TriageQueue::HasTasks() { | 
|  | DCHECK_CALLED_ON_VALID_SEQUENCE(outer_->sequence_checker_); | 
|  | ReloadFromIncomingQueueIfEmpty(); | 
|  | return !queue_.empty(); | 
|  | } | 
|  |  | 
|  | void IncomingTaskQueue::TriageQueue::Clear() { | 
|  | DCHECK_CALLED_ON_VALID_SEQUENCE(outer_->sequence_checker_); | 
|  | // Previously, MessageLoop would delete all tasks including delayed and | 
|  | // deferred tasks in a single round before attempting to reload from the | 
|  | // incoming queue to see if more tasks remained. This gave it a chance to | 
|  | // assess whether or not clearing should continue. As a result, while | 
|  | // reloading is automatic for getting and seeing if tasks exist, it is not | 
|  | // automatic for Clear(). | 
|  | while (!queue_.empty()) { | 
|  | PendingTask pending_task = std::move(queue_.front()); | 
|  | queue_.pop(); | 
|  |  | 
|  | if (pending_task.is_high_res) | 
|  | --outer_->pending_high_res_tasks_; | 
|  |  | 
|  | if (!pending_task.delayed_run_time.is_null()) { | 
|  | outer_->delayed_tasks().Push(std::move(pending_task)); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void IncomingTaskQueue::TriageQueue::ReloadFromIncomingQueueIfEmpty() { | 
|  | DCHECK_CALLED_ON_VALID_SEQUENCE(outer_->sequence_checker_); | 
|  | if (queue_.empty()) { | 
|  | // TODO(robliao): Since these high resolution tasks aren't yet in the | 
|  | // delayed queue, they technically shouldn't trigger high resolution timers | 
|  | // until they are. | 
|  | outer_->pending_high_res_tasks_ += outer_->ReloadWorkQueue(&queue_); | 
|  | } | 
|  | } | 
|  |  | 
|  | IncomingTaskQueue::DelayedQueue::DelayedQueue(IncomingTaskQueue* outer) | 
|  | : outer_(outer) {} | 
|  |  | 
|  | IncomingTaskQueue::DelayedQueue::~DelayedQueue() = default; | 
|  |  | 
|  | void IncomingTaskQueue::DelayedQueue::Push(PendingTask pending_task) { | 
|  | DCHECK_CALLED_ON_VALID_SEQUENCE(outer_->sequence_checker_); | 
|  |  | 
|  | if (pending_task.is_high_res) | 
|  | ++outer_->pending_high_res_tasks_; | 
|  |  | 
|  | queue_.push(std::move(pending_task)); | 
|  | } | 
|  |  | 
|  | const PendingTask& IncomingTaskQueue::DelayedQueue::Peek() { | 
|  | DCHECK_CALLED_ON_VALID_SEQUENCE(outer_->sequence_checker_); | 
|  | DCHECK(!queue_.empty()); | 
|  | return queue_.top(); | 
|  | } | 
|  |  | 
|  | PendingTask IncomingTaskQueue::DelayedQueue::Pop() { | 
|  | DCHECK_CALLED_ON_VALID_SEQUENCE(outer_->sequence_checker_); | 
|  | DCHECK(!queue_.empty()); | 
|  | PendingTask delayed_task = std::move(const_cast<PendingTask&>(queue_.top())); | 
|  | queue_.pop(); | 
|  |  | 
|  | if (delayed_task.is_high_res) | 
|  | --outer_->pending_high_res_tasks_; | 
|  |  | 
|  | return delayed_task; | 
|  | } | 
|  |  | 
|  | bool IncomingTaskQueue::DelayedQueue::HasTasks() { | 
|  | DCHECK_CALLED_ON_VALID_SEQUENCE(outer_->sequence_checker_); | 
|  | // TODO(robliao): The other queues don't check for IsCancelled(). Should they? | 
|  | while (!queue_.empty() && Peek().task.IsCancelled()) | 
|  | Pop(); | 
|  |  | 
|  | return !queue_.empty(); | 
|  | } | 
|  |  | 
|  | void IncomingTaskQueue::DelayedQueue::Clear() { | 
|  | DCHECK_CALLED_ON_VALID_SEQUENCE(outer_->sequence_checker_); | 
|  | while (!queue_.empty()) | 
|  | Pop(); | 
|  | } | 
|  |  | 
|  | IncomingTaskQueue::DeferredQueue::DeferredQueue(IncomingTaskQueue* outer) | 
|  | : outer_(outer) {} | 
|  |  | 
|  | IncomingTaskQueue::DeferredQueue::~DeferredQueue() = default; | 
|  |  | 
|  | void IncomingTaskQueue::DeferredQueue::Push(PendingTask pending_task) { | 
|  | DCHECK_CALLED_ON_VALID_SEQUENCE(outer_->sequence_checker_); | 
|  |  | 
|  | // TODO(robliao): These tasks should not count towards the high res task count | 
|  | // since they are no longer in the delayed queue. | 
|  | if (pending_task.is_high_res) | 
|  | ++outer_->pending_high_res_tasks_; | 
|  |  | 
|  | queue_.push(std::move(pending_task)); | 
|  | } | 
|  |  | 
|  | const PendingTask& IncomingTaskQueue::DeferredQueue::Peek() { | 
|  | DCHECK_CALLED_ON_VALID_SEQUENCE(outer_->sequence_checker_); | 
|  | DCHECK(!queue_.empty()); | 
|  | return queue_.front(); | 
|  | } | 
|  |  | 
|  | PendingTask IncomingTaskQueue::DeferredQueue::Pop() { | 
|  | DCHECK_CALLED_ON_VALID_SEQUENCE(outer_->sequence_checker_); | 
|  | DCHECK(!queue_.empty()); | 
|  | PendingTask deferred_task = std::move(queue_.front()); | 
|  | queue_.pop(); | 
|  |  | 
|  | if (deferred_task.is_high_res) | 
|  | --outer_->pending_high_res_tasks_; | 
|  |  | 
|  | return deferred_task; | 
|  | } | 
|  |  | 
|  | bool IncomingTaskQueue::DeferredQueue::HasTasks() { | 
|  | DCHECK_CALLED_ON_VALID_SEQUENCE(outer_->sequence_checker_); | 
|  | return !queue_.empty(); | 
|  | } | 
|  |  | 
|  | void IncomingTaskQueue::DeferredQueue::Clear() { | 
|  | DCHECK_CALLED_ON_VALID_SEQUENCE(outer_->sequence_checker_); | 
|  | while (!queue_.empty()) | 
|  | Pop(); | 
|  | } | 
|  |  | 
|  | bool IncomingTaskQueue::PostPendingTask(PendingTask* pending_task) { | 
|  | // 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. | 
|  | bool accept_new_tasks; | 
|  | bool schedule_work = false; | 
|  | { | 
|  | AutoLock auto_lock(incoming_queue_lock_); | 
|  | accept_new_tasks = accept_new_tasks_; | 
|  | if (accept_new_tasks) | 
|  | schedule_work = PostPendingTaskLockRequired(pending_task); | 
|  | } | 
|  |  | 
|  | if (!accept_new_tasks) { | 
|  | // Clear the pending task outside of |incoming_queue_lock_| to prevent any | 
|  | // chance of self-deadlock if destroying a task also posts a task to this | 
|  | // queue. | 
|  | DCHECK(!schedule_work); | 
|  | pending_task->task.Reset(); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Wake up the message loop and schedule work. This is done outside | 
|  | // |incoming_queue_lock_| to allow for multiple post tasks to occur while | 
|  | // ScheduleWork() is running. For platforms (e.g. Android) that require one | 
|  | // call to ScheduleWork() for each task, all pending tasks may serialize | 
|  | // within the ScheduleWork() call. As a result, holding a lock to maintain the | 
|  | // lifetime of |message_loop_| is less of a concern. | 
|  | if (schedule_work) { | 
|  | // Ensures |message_loop_| isn't destroyed while running. | 
|  | AutoLock auto_lock(message_loop_lock_); | 
|  | if (message_loop_) | 
|  | message_loop_->ScheduleWork(); | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool IncomingTaskQueue::PostPendingTaskLockRequired(PendingTask* pending_task) { | 
|  | incoming_queue_lock_.AssertAcquired(); | 
|  |  | 
|  | #if defined(OS_WIN) | 
|  | if (pending_task->is_high_res) | 
|  | ++high_res_task_count_; | 
|  | #endif | 
|  |  | 
|  | // Initialize the sequence number. The sequence number is used for delayed | 
|  | // tasks (to facilitate FIFO sorting when two tasks have the same | 
|  | // delayed_run_time value) and for identifying the task in about:tracing. | 
|  | pending_task->sequence_num = next_sequence_num_++; | 
|  |  | 
|  | task_annotator_.DidQueueTask("MessageLoop::PostTask", *pending_task); | 
|  |  | 
|  | bool was_empty = incoming_queue_.empty(); | 
|  | incoming_queue_.push(std::move(*pending_task)); | 
|  |  | 
|  | if (is_ready_for_scheduling_ && | 
|  | (always_schedule_work_ || (!message_loop_scheduled_ && was_empty))) { | 
|  | // 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; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | int IncomingTaskQueue::ReloadWorkQueue(TaskQueue* work_queue) { | 
|  | DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | 
|  |  | 
|  | // Make sure no tasks are lost. | 
|  | DCHECK(work_queue->empty()); | 
|  |  | 
|  | // Acquire all we can from the inter-thread queue with one lock acquisition. | 
|  | AutoLock lock(incoming_queue_lock_); | 
|  | 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; | 
|  | return high_res_tasks; | 
|  | } | 
|  |  | 
|  | }  // namespace internal | 
|  | }  // namespace base |