| // Copyright (c) 2012 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/message_loop_task_runner.h" |
| |
| #include <utility> |
| |
| #include "base/location.h" |
| #include "base/logging.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "build/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 |
| |
| TimeTicks CalculateDelayedRuntime(const Location& from_here, TimeDelta delay) { |
| DLOG_IF(WARNING, delay > kTaskDelayWarningThreshold) |
| << "Requesting super-long task delay period of " << delay.InSeconds() |
| << " seconds from here: " << from_here.ToString(); |
| |
| DCHECK_GE(delay, TimeDelta()) << "delay should not be negative"; |
| |
| return delay > TimeDelta() ? TimeTicks::Now() + delay : TimeTicks(); |
| } |
| |
| } // namespace |
| |
| MessageLoopTaskRunner::MessageLoopTaskRunner( |
| std::unique_ptr<SequencedTaskSource::Observer> task_source_observer) |
| : task_source_observer_(std::move(task_source_observer)) {} |
| |
| void MessageLoopTaskRunner::BindToCurrentThread() { |
| AutoLock lock(valid_thread_id_lock_); |
| DCHECK_EQ(kInvalidThreadId, valid_thread_id_); |
| valid_thread_id_ = PlatformThread::CurrentId(); |
| } |
| |
| void MessageLoopTaskRunner::Shutdown() { |
| AutoLock lock(incoming_queue_lock_); |
| accept_new_tasks_ = false; |
| #if defined(STARBOARD) |
| // Bug: base MessageLoopTaskRunner does not clear its pending task queues at |
| // exit. |
| while (!outgoing_queue_.empty()) { |
| outgoing_queue_.pop(); |
| } |
| while (!incoming_queue_.empty()) { |
| incoming_queue_.pop(); |
| } |
| #endif |
| } |
| |
| bool MessageLoopTaskRunner::PostDelayedTask(const Location& from_here, |
| OnceClosure task, |
| base::TimeDelta delay) { |
| return AddToIncomingQueue(from_here, std::move(task), delay, |
| Nestable::kNestable); |
| } |
| |
| bool MessageLoopTaskRunner::PostNonNestableDelayedTask( |
| const Location& from_here, |
| OnceClosure task, |
| base::TimeDelta delay) { |
| return AddToIncomingQueue(from_here, std::move(task), delay, |
| Nestable::kNonNestable); |
| } |
| |
| bool MessageLoopTaskRunner::RunsTasksInCurrentSequence() const { |
| AutoLock lock(valid_thread_id_lock_); |
| return valid_thread_id_ == PlatformThread::CurrentId(); |
| } |
| |
| PendingTask MessageLoopTaskRunner::TakeTask() { |
| // Must be called on execution sequence, unless clearing tasks from an unbound |
| // MessageLoop. |
| DCHECK(RunsTasksInCurrentSequence() || valid_thread_id_ == kInvalidThreadId); |
| |
| // HasTasks() will reload the queue if necessary (there should always be |
| // pending tasks by contract). |
| const bool has_tasks = HasTasks(); |
| DCHECK(has_tasks); |
| |
| PendingTask pending_task = std::move(outgoing_queue_.front()); |
| outgoing_queue_.pop(); |
| return pending_task; |
| } |
| |
| bool MessageLoopTaskRunner::HasTasks() { |
| // Must be called on execution sequence, unless clearing tasks from an unbound |
| // MessageLoop. |
| DCHECK(RunsTasksInCurrentSequence() || valid_thread_id_ == kInvalidThreadId); |
| |
| if (outgoing_queue_.empty()) { |
| AutoLock lock(incoming_queue_lock_); |
| incoming_queue_.swap(outgoing_queue_); |
| outgoing_queue_empty_ = outgoing_queue_.empty(); |
| } |
| |
| return !outgoing_queue_.empty(); |
| } |
| |
| void MessageLoopTaskRunner::InjectTask(OnceClosure task) { |
| // Must be called on execution sequence, unless clearing tasks from an unbound |
| // MessageLoop. |
| DCHECK(RunsTasksInCurrentSequence() || valid_thread_id_ == kInvalidThreadId); |
| |
| bool success = this->PostTask(FROM_HERE, std::move(task)); |
| DCHECK(success) << "Injected a task in a dead task runner."; |
| } |
| |
| void MessageLoopTaskRunner::SetAddQueueTimeToTasks(bool enable) { |
| base::subtle::NoBarrier_Store(&add_queue_time_to_tasks_, enable ? 1 : 0); |
| } |
| |
| MessageLoopTaskRunner::~MessageLoopTaskRunner() = default; |
| |
| bool MessageLoopTaskRunner::AddToIncomingQueue(const Location& from_here, |
| OnceClosure task, |
| TimeDelta delay, |
| Nestable nestable) { |
| DCHECK(task_source_observer_) |
| << "SetObserver() must be called before posting tasks"; |
| |
| // Use CHECK instead of DCHECK to crash earlier. See http://crbug.com/711167 |
| // for details. |
| CHECK(task); |
| |
| PendingTask pending_task(from_here, std::move(task), |
| CalculateDelayedRuntime(from_here, delay), nestable); |
| |
| if (base::subtle::NoBarrier_Load(&add_queue_time_to_tasks_)) { |
| if (pending_task.delayed_run_time.is_null()) { |
| pending_task.queue_time = base::TimeTicks::Now(); |
| } else { |
| pending_task.queue_time = pending_task.delayed_run_time - delay; |
| } |
| } |
| |
| #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 |
| |
| bool did_queue_task = false; |
| bool was_empty; |
| { |
| AutoLock auto_lock(incoming_queue_lock_); |
| if (accept_new_tasks_) { |
| // 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_source_observer_->WillQueueTask(&pending_task); |
| |
| was_empty = outgoing_queue_empty_ && incoming_queue_.empty(); |
| incoming_queue_.push(std::move(pending_task)); |
| |
| did_queue_task = true; |
| } |
| } |
| |
| if (!did_queue_task) { |
| // 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. |
| pending_task.task.Reset(); |
| return false; |
| } |
| |
| // Let |task_source_observer_| know about the task just queued. It's important |
| // to do this outside of |incoming_queue_lock_| to avoid blocking tasks |
| // incoming from other threads on the resolution of DidQueueTask(). |
| task_source_observer_->DidQueueTask(was_empty); |
| return true; |
| } |
| |
| } // namespace internal |
| |
| } // namespace base |