blob: 14a568e66f902eb694b29b1c65785234baf3333f [file] [log] [blame]
// 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