mirror of
https://github.com/klzgrad/naiveproxy.git
synced 2024-11-24 14:26:09 +03:00
498 lines
16 KiB
C++
498 lines
16 KiB
C++
// Copyright 2015 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/test/test_mock_time_task_runner.h"
|
|
|
|
#include <utility>
|
|
|
|
#include "base/containers/circular_deque.h"
|
|
#include "base/logging.h"
|
|
#include "base/macros.h"
|
|
#include "base/memory/ptr_util.h"
|
|
#include "base/memory/ref_counted.h"
|
|
#include "base/threading/thread_task_runner_handle.h"
|
|
|
|
namespace base {
|
|
namespace {
|
|
|
|
// LegacyMockTickClock and LegacyMockClock are used by deprecated APIs of
|
|
// TestMockTimeTaskRunner. They will be removed after updating callers of
|
|
// GetMockClock() and GetMockTickClock() to GetMockClockPtr() and
|
|
// GetMockTickClockPtr().
|
|
class LegacyMockTickClock : public TickClock {
|
|
public:
|
|
explicit LegacyMockTickClock(
|
|
scoped_refptr<const TestMockTimeTaskRunner> task_runner)
|
|
: task_runner_(std::move(task_runner)) {}
|
|
|
|
// TickClock:
|
|
TimeTicks NowTicks() const override { return task_runner_->NowTicks(); }
|
|
|
|
private:
|
|
scoped_refptr<const TestMockTimeTaskRunner> task_runner_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(LegacyMockTickClock);
|
|
};
|
|
|
|
class LegacyMockClock : public Clock {
|
|
public:
|
|
explicit LegacyMockClock(
|
|
scoped_refptr<const TestMockTimeTaskRunner> task_runner)
|
|
: task_runner_(std::move(task_runner)) {}
|
|
|
|
// Clock:
|
|
Time Now() const override { return task_runner_->Now(); }
|
|
|
|
private:
|
|
scoped_refptr<const TestMockTimeTaskRunner> task_runner_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(LegacyMockClock);
|
|
};
|
|
|
|
} // namespace
|
|
|
|
// A SingleThreadTaskRunner which forwards everything to its |target_|. This
|
|
// serves two purposes:
|
|
// 1) If a ThreadTaskRunnerHandle owned by TestMockTimeTaskRunner were to be
|
|
// set to point to that TestMockTimeTaskRunner, a reference cycle would
|
|
// result. As |target_| here is a non-refcounting raw pointer, the cycle is
|
|
// broken.
|
|
// 2) Since SingleThreadTaskRunner is ref-counted, it's quite easy for it to
|
|
// accidentally get captured between tests in a singleton somewhere.
|
|
// Indirecting via NonOwningProxyTaskRunner permits TestMockTimeTaskRunner
|
|
// to be cleaned up (removing the RunLoop::Delegate in the kBoundToThread
|
|
// mode), and to also cleanly flag any actual attempts to use the leaked
|
|
// task runner.
|
|
class TestMockTimeTaskRunner::NonOwningProxyTaskRunner
|
|
: public SingleThreadTaskRunner {
|
|
public:
|
|
explicit NonOwningProxyTaskRunner(SingleThreadTaskRunner* target)
|
|
: target_(target) {
|
|
DCHECK(target_);
|
|
}
|
|
|
|
// Detaches this NonOwningProxyTaskRunner instance from its |target_|. It is
|
|
// invalid to post tasks after this point but RunsTasksInCurrentSequence()
|
|
// will still pass on the original thread for convenience with legacy code.
|
|
void Detach() {
|
|
AutoLock scoped_lock(lock_);
|
|
target_ = nullptr;
|
|
}
|
|
|
|
// SingleThreadTaskRunner:
|
|
bool RunsTasksInCurrentSequence() const override {
|
|
AutoLock scoped_lock(lock_);
|
|
if (target_)
|
|
return target_->RunsTasksInCurrentSequence();
|
|
return thread_checker_.CalledOnValidThread();
|
|
}
|
|
|
|
bool PostDelayedTask(const Location& from_here,
|
|
OnceClosure task,
|
|
TimeDelta delay) override {
|
|
AutoLock scoped_lock(lock_);
|
|
if (target_)
|
|
return target_->PostDelayedTask(from_here, std::move(task), delay);
|
|
|
|
// The associated TestMockTimeTaskRunner is dead, so fail this PostTask.
|
|
return false;
|
|
}
|
|
|
|
bool PostNonNestableDelayedTask(const Location& from_here,
|
|
OnceClosure task,
|
|
TimeDelta delay) override {
|
|
AutoLock scoped_lock(lock_);
|
|
if (target_) {
|
|
return target_->PostNonNestableDelayedTask(from_here, std::move(task),
|
|
delay);
|
|
}
|
|
|
|
// The associated TestMockTimeTaskRunner is dead, so fail this PostTask.
|
|
return false;
|
|
}
|
|
|
|
private:
|
|
friend class RefCountedThreadSafe<NonOwningProxyTaskRunner>;
|
|
~NonOwningProxyTaskRunner() override = default;
|
|
|
|
mutable Lock lock_;
|
|
SingleThreadTaskRunner* target_; // guarded by lock_
|
|
|
|
// Used to implement RunsTasksInCurrentSequence, without relying on |target_|.
|
|
ThreadCheckerImpl thread_checker_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(NonOwningProxyTaskRunner);
|
|
};
|
|
|
|
// TestMockTimeTaskRunner::TestOrderedPendingTask -----------------------------
|
|
|
|
// Subclass of TestPendingTask which has a strictly monotonically increasing ID
|
|
// for every task, so that tasks posted with the same 'time to run' can be run
|
|
// in the order of being posted.
|
|
struct TestMockTimeTaskRunner::TestOrderedPendingTask
|
|
: public base::TestPendingTask {
|
|
TestOrderedPendingTask();
|
|
TestOrderedPendingTask(const Location& location,
|
|
OnceClosure task,
|
|
TimeTicks post_time,
|
|
TimeDelta delay,
|
|
size_t ordinal,
|
|
TestNestability nestability);
|
|
TestOrderedPendingTask(TestOrderedPendingTask&&);
|
|
~TestOrderedPendingTask();
|
|
|
|
TestOrderedPendingTask& operator=(TestOrderedPendingTask&&);
|
|
|
|
size_t ordinal;
|
|
|
|
private:
|
|
DISALLOW_COPY_AND_ASSIGN(TestOrderedPendingTask);
|
|
};
|
|
|
|
TestMockTimeTaskRunner::TestOrderedPendingTask::TestOrderedPendingTask()
|
|
: ordinal(0) {
|
|
}
|
|
|
|
TestMockTimeTaskRunner::TestOrderedPendingTask::TestOrderedPendingTask(
|
|
TestOrderedPendingTask&&) = default;
|
|
|
|
TestMockTimeTaskRunner::TestOrderedPendingTask::TestOrderedPendingTask(
|
|
const Location& location,
|
|
OnceClosure task,
|
|
TimeTicks post_time,
|
|
TimeDelta delay,
|
|
size_t ordinal,
|
|
TestNestability nestability)
|
|
: base::TestPendingTask(location,
|
|
std::move(task),
|
|
post_time,
|
|
delay,
|
|
nestability),
|
|
ordinal(ordinal) {}
|
|
|
|
TestMockTimeTaskRunner::TestOrderedPendingTask::~TestOrderedPendingTask() =
|
|
default;
|
|
|
|
TestMockTimeTaskRunner::TestOrderedPendingTask&
|
|
TestMockTimeTaskRunner::TestOrderedPendingTask::operator=(
|
|
TestOrderedPendingTask&&) = default;
|
|
|
|
// TestMockTimeTaskRunner -----------------------------------------------------
|
|
|
|
// TODO(gab): This should also set the SequenceToken for the current thread.
|
|
// Ref. TestMockTimeTaskRunner::RunsTasksInCurrentSequence().
|
|
TestMockTimeTaskRunner::ScopedContext::ScopedContext(
|
|
scoped_refptr<TestMockTimeTaskRunner> scope)
|
|
: on_destroy_(ThreadTaskRunnerHandle::OverrideForTesting(scope)) {
|
|
scope->RunUntilIdle();
|
|
}
|
|
|
|
TestMockTimeTaskRunner::ScopedContext::~ScopedContext() = default;
|
|
|
|
bool TestMockTimeTaskRunner::TemporalOrder::operator()(
|
|
const TestOrderedPendingTask& first_task,
|
|
const TestOrderedPendingTask& second_task) const {
|
|
if (first_task.GetTimeToRun() == second_task.GetTimeToRun())
|
|
return first_task.ordinal > second_task.ordinal;
|
|
return first_task.GetTimeToRun() > second_task.GetTimeToRun();
|
|
}
|
|
|
|
TestMockTimeTaskRunner::TestMockTimeTaskRunner(Type type)
|
|
: TestMockTimeTaskRunner(Time::UnixEpoch(), TimeTicks(), type) {}
|
|
|
|
TestMockTimeTaskRunner::TestMockTimeTaskRunner(Time start_time,
|
|
TimeTicks start_ticks,
|
|
Type type)
|
|
: now_(start_time),
|
|
now_ticks_(start_ticks),
|
|
tasks_lock_cv_(&tasks_lock_),
|
|
proxy_task_runner_(MakeRefCounted<NonOwningProxyTaskRunner>(this)),
|
|
mock_clock_(this) {
|
|
if (type == Type::kBoundToThread) {
|
|
RunLoop::RegisterDelegateForCurrentThread(this);
|
|
thread_task_runner_handle_ =
|
|
std::make_unique<ThreadTaskRunnerHandle>(proxy_task_runner_);
|
|
}
|
|
}
|
|
|
|
TestMockTimeTaskRunner::~TestMockTimeTaskRunner() {
|
|
proxy_task_runner_->Detach();
|
|
}
|
|
|
|
void TestMockTimeTaskRunner::FastForwardBy(TimeDelta delta) {
|
|
DCHECK(thread_checker_.CalledOnValidThread());
|
|
DCHECK_GE(delta, TimeDelta());
|
|
|
|
const TimeTicks original_now_ticks = NowTicks();
|
|
ProcessAllTasksNoLaterThan(delta);
|
|
ForwardClocksUntilTickTime(original_now_ticks + delta);
|
|
}
|
|
|
|
void TestMockTimeTaskRunner::AdvanceMockTickClock(TimeDelta delta) {
|
|
ForwardClocksUntilTickTime(NowTicks() + delta);
|
|
}
|
|
|
|
void TestMockTimeTaskRunner::RunUntilIdle() {
|
|
DCHECK(thread_checker_.CalledOnValidThread());
|
|
ProcessAllTasksNoLaterThan(TimeDelta());
|
|
}
|
|
|
|
void TestMockTimeTaskRunner::FastForwardUntilNoTasksRemain() {
|
|
DCHECK(thread_checker_.CalledOnValidThread());
|
|
ProcessAllTasksNoLaterThan(TimeDelta::Max());
|
|
}
|
|
|
|
void TestMockTimeTaskRunner::ClearPendingTasks() {
|
|
AutoLock scoped_lock(tasks_lock_);
|
|
// This is repeated in case task destruction triggers further tasks.
|
|
while (!tasks_.empty()) {
|
|
TaskPriorityQueue cleanup_tasks;
|
|
tasks_.swap(cleanup_tasks);
|
|
|
|
// Destroy task objects with |tasks_lock_| released. Task deletion can cause
|
|
// calls to NonOwningProxyTaskRunner::RunsTasksInCurrentSequence()
|
|
// (e.g. for DCHECKs), which causes |NonOwningProxyTaskRunner::lock_| to be
|
|
// grabbed.
|
|
//
|
|
// On the other hand, calls from NonOwningProxyTaskRunner::PostTask ->
|
|
// TestMockTimeTaskRunner::PostTask acquire locks as
|
|
// |NonOwningProxyTaskRunner::lock_| followed by |tasks_lock_|, so it's
|
|
// desirable to avoid the reverse order, for deadlock freedom.
|
|
AutoUnlock scoped_unlock(tasks_lock_);
|
|
while (!cleanup_tasks.empty())
|
|
cleanup_tasks.pop();
|
|
}
|
|
}
|
|
|
|
Time TestMockTimeTaskRunner::Now() const {
|
|
AutoLock scoped_lock(tasks_lock_);
|
|
return now_;
|
|
}
|
|
|
|
TimeTicks TestMockTimeTaskRunner::NowTicks() const {
|
|
AutoLock scoped_lock(tasks_lock_);
|
|
return now_ticks_;
|
|
}
|
|
|
|
std::unique_ptr<Clock> TestMockTimeTaskRunner::DeprecatedGetMockClock() const {
|
|
DCHECK(thread_checker_.CalledOnValidThread());
|
|
return std::make_unique<LegacyMockClock>(this);
|
|
}
|
|
|
|
Clock* TestMockTimeTaskRunner::GetMockClock() const {
|
|
DCHECK(thread_checker_.CalledOnValidThread());
|
|
return &mock_clock_;
|
|
}
|
|
|
|
std::unique_ptr<TickClock> TestMockTimeTaskRunner::DeprecatedGetMockTickClock()
|
|
const {
|
|
DCHECK(thread_checker_.CalledOnValidThread());
|
|
return std::make_unique<LegacyMockTickClock>(this);
|
|
}
|
|
|
|
const TickClock* TestMockTimeTaskRunner::GetMockTickClock() const {
|
|
DCHECK(thread_checker_.CalledOnValidThread());
|
|
return &mock_clock_;
|
|
}
|
|
|
|
base::circular_deque<TestPendingTask>
|
|
TestMockTimeTaskRunner::TakePendingTasks() {
|
|
AutoLock scoped_lock(tasks_lock_);
|
|
base::circular_deque<TestPendingTask> tasks;
|
|
while (!tasks_.empty()) {
|
|
// It's safe to remove const and consume |task| here, since |task| is not
|
|
// used for ordering the item.
|
|
if (!tasks_.top().task.IsCancelled()) {
|
|
tasks.push_back(
|
|
std::move(const_cast<TestOrderedPendingTask&>(tasks_.top())));
|
|
}
|
|
tasks_.pop();
|
|
}
|
|
return tasks;
|
|
}
|
|
|
|
bool TestMockTimeTaskRunner::HasPendingTask() {
|
|
DCHECK(thread_checker_.CalledOnValidThread());
|
|
AutoLock scoped_lock(tasks_lock_);
|
|
while (!tasks_.empty() && tasks_.top().task.IsCancelled())
|
|
tasks_.pop();
|
|
return !tasks_.empty();
|
|
}
|
|
|
|
size_t TestMockTimeTaskRunner::GetPendingTaskCount() {
|
|
DCHECK(thread_checker_.CalledOnValidThread());
|
|
AutoLock scoped_lock(tasks_lock_);
|
|
TaskPriorityQueue preserved_tasks;
|
|
while (!tasks_.empty()) {
|
|
if (!tasks_.top().task.IsCancelled()) {
|
|
preserved_tasks.push(
|
|
std::move(const_cast<TestOrderedPendingTask&>(tasks_.top())));
|
|
}
|
|
tasks_.pop();
|
|
}
|
|
tasks_.swap(preserved_tasks);
|
|
return tasks_.size();
|
|
}
|
|
|
|
TimeDelta TestMockTimeTaskRunner::NextPendingTaskDelay() {
|
|
DCHECK(thread_checker_.CalledOnValidThread());
|
|
AutoLock scoped_lock(tasks_lock_);
|
|
while (!tasks_.empty() && tasks_.top().task.IsCancelled())
|
|
tasks_.pop();
|
|
return tasks_.empty() ? TimeDelta::Max()
|
|
: tasks_.top().GetTimeToRun() - now_ticks_;
|
|
}
|
|
|
|
// TODO(gab): Combine |thread_checker_| with a SequenceToken to differentiate
|
|
// between tasks running in the scope of this TestMockTimeTaskRunner and other
|
|
// task runners sharing this thread. http://crbug.com/631186
|
|
bool TestMockTimeTaskRunner::RunsTasksInCurrentSequence() const {
|
|
return thread_checker_.CalledOnValidThread();
|
|
}
|
|
|
|
bool TestMockTimeTaskRunner::PostDelayedTask(const Location& from_here,
|
|
OnceClosure task,
|
|
TimeDelta delay) {
|
|
AutoLock scoped_lock(tasks_lock_);
|
|
tasks_.push(TestOrderedPendingTask(from_here, std::move(task), now_ticks_,
|
|
delay, next_task_ordinal_++,
|
|
TestPendingTask::NESTABLE));
|
|
tasks_lock_cv_.Signal();
|
|
return true;
|
|
}
|
|
|
|
bool TestMockTimeTaskRunner::PostNonNestableDelayedTask(
|
|
const Location& from_here,
|
|
OnceClosure task,
|
|
TimeDelta delay) {
|
|
return PostDelayedTask(from_here, std::move(task), delay);
|
|
}
|
|
|
|
void TestMockTimeTaskRunner::OnBeforeSelectingTask() {
|
|
// Empty default implementation.
|
|
}
|
|
|
|
void TestMockTimeTaskRunner::OnAfterTimePassed() {
|
|
// Empty default implementation.
|
|
}
|
|
|
|
void TestMockTimeTaskRunner::OnAfterTaskRun() {
|
|
// Empty default implementation.
|
|
}
|
|
|
|
void TestMockTimeTaskRunner::ProcessAllTasksNoLaterThan(TimeDelta max_delta) {
|
|
DCHECK(thread_checker_.CalledOnValidThread());
|
|
DCHECK_GE(max_delta, TimeDelta());
|
|
|
|
// Multiple test task runners can share the same thread for determinism in
|
|
// unit tests. Make sure this TestMockTimeTaskRunner's tasks run in its scope.
|
|
ScopedClosureRunner undo_override;
|
|
if (!ThreadTaskRunnerHandle::IsSet() ||
|
|
ThreadTaskRunnerHandle::Get() != proxy_task_runner_.get()) {
|
|
undo_override =
|
|
ThreadTaskRunnerHandle::OverrideForTesting(proxy_task_runner_.get());
|
|
}
|
|
|
|
const TimeTicks original_now_ticks = NowTicks();
|
|
while (!quit_run_loop_) {
|
|
OnBeforeSelectingTask();
|
|
TestPendingTask task_info;
|
|
if (!DequeueNextTask(original_now_ticks, max_delta, &task_info))
|
|
break;
|
|
if (task_info.task.IsCancelled())
|
|
continue;
|
|
// If tasks were posted with a negative delay, task_info.GetTimeToRun() will
|
|
// be less than |now_ticks_|. ForwardClocksUntilTickTime() takes care of not
|
|
// moving the clock backwards in this case.
|
|
ForwardClocksUntilTickTime(task_info.GetTimeToRun());
|
|
std::move(task_info.task).Run();
|
|
OnAfterTaskRun();
|
|
}
|
|
}
|
|
|
|
void TestMockTimeTaskRunner::ForwardClocksUntilTickTime(TimeTicks later_ticks) {
|
|
DCHECK(thread_checker_.CalledOnValidThread());
|
|
{
|
|
AutoLock scoped_lock(tasks_lock_);
|
|
if (later_ticks <= now_ticks_)
|
|
return;
|
|
|
|
now_ += later_ticks - now_ticks_;
|
|
now_ticks_ = later_ticks;
|
|
}
|
|
OnAfterTimePassed();
|
|
}
|
|
|
|
bool TestMockTimeTaskRunner::DequeueNextTask(const TimeTicks& reference,
|
|
const TimeDelta& max_delta,
|
|
TestPendingTask* next_task) {
|
|
DCHECK(thread_checker_.CalledOnValidThread());
|
|
AutoLock scoped_lock(tasks_lock_);
|
|
if (!tasks_.empty() &&
|
|
(tasks_.top().GetTimeToRun() - reference) <= max_delta) {
|
|
// It's safe to remove const and consume |task| here, since |task| is not
|
|
// used for ordering the item.
|
|
*next_task = std::move(const_cast<TestOrderedPendingTask&>(tasks_.top()));
|
|
tasks_.pop();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void TestMockTimeTaskRunner::Run(bool application_tasks_allowed) {
|
|
DCHECK(thread_checker_.CalledOnValidThread());
|
|
|
|
// Since TestMockTimeTaskRunner doesn't process system messages: there's no
|
|
// hope for anything but an application task to call Quit(). If this RunLoop
|
|
// can't process application tasks (i.e. disallowed by default in nested
|
|
// RunLoops) it's guaranteed to hang...
|
|
DCHECK(application_tasks_allowed)
|
|
<< "This is a nested RunLoop instance and needs to be of "
|
|
"Type::kNestableTasksAllowed.";
|
|
|
|
while (!quit_run_loop_) {
|
|
RunUntilIdle();
|
|
if (quit_run_loop_ || ShouldQuitWhenIdle())
|
|
break;
|
|
|
|
// Peek into |tasks_| to perform one of two things:
|
|
// A) If there are no remaining tasks, wait until one is posted and
|
|
// restart from the top.
|
|
// B) If there is a remaining delayed task. Fast-forward to reach the next
|
|
// round of tasks.
|
|
TimeDelta auto_fast_forward_by;
|
|
{
|
|
AutoLock scoped_lock(tasks_lock_);
|
|
if (tasks_.empty()) {
|
|
while (tasks_.empty())
|
|
tasks_lock_cv_.Wait();
|
|
continue;
|
|
}
|
|
auto_fast_forward_by = tasks_.top().GetTimeToRun() - now_ticks_;
|
|
}
|
|
FastForwardBy(auto_fast_forward_by);
|
|
}
|
|
quit_run_loop_ = false;
|
|
}
|
|
|
|
void TestMockTimeTaskRunner::Quit() {
|
|
DCHECK(thread_checker_.CalledOnValidThread());
|
|
quit_run_loop_ = true;
|
|
}
|
|
|
|
void TestMockTimeTaskRunner::EnsureWorkScheduled() {
|
|
// Nothing to do: TestMockTimeTaskRunner::Run() will always process tasks and
|
|
// doesn't need an extra kick on nested runs.
|
|
}
|
|
|
|
TimeTicks TestMockTimeTaskRunner::MockClock::NowTicks() const {
|
|
return task_runner_->NowTicks();
|
|
}
|
|
|
|
Time TestMockTimeTaskRunner::MockClock::Now() const {
|
|
return task_runner_->Now();
|
|
}
|
|
|
|
} // namespace base
|