mirror of
https://github.com/klzgrad/naiveproxy.git
synced 2024-12-01 01:36:09 +03:00
186 lines
6.2 KiB
C++
186 lines
6.2 KiB
C++
|
// 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;
|
||
|
}
|
||
|
|
||
|
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) {
|
||
|
DCHECK_NE(add_queue_time_to_tasks_, enable);
|
||
|
add_queue_time_to_tasks_ = enable;
|
||
|
}
|
||
|
|
||
|
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 (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
|