123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482 |
- // 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/check_op.h"
- #include "base/containers/circular_deque.h"
- #include "base/memory/ptr_util.h"
- #include "base/memory/raw_ptr.h"
- #include "base/memory/ref_counted.h"
- #include "base/threading/thread_task_runner_handle.h"
- #include "third_party/abseil-cpp/absl/types/optional.h"
- namespace base {
- // 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_);
- }
- NonOwningProxyTaskRunner(const NonOwningProxyTaskRunner&) = delete;
- NonOwningProxyTaskRunner& operator=(const NonOwningProxyTaskRunner&) = delete;
- // 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_;
- raw_ptr<SingleThreadTaskRunner> target_; // guarded by lock_
- // Used to implement RunsTasksInCurrentSequence, without relying on |target_|.
- ThreadCheckerImpl thread_checker_;
- };
- // 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(const TestOrderedPendingTask&) = delete;
- TestOrderedPendingTask& operator=(const TestOrderedPendingTask&) = delete;
- TestOrderedPendingTask(TestOrderedPendingTask&&);
- ~TestOrderedPendingTask();
- TestOrderedPendingTask& operator=(TestOrderedPendingTask&&);
- size_t ordinal;
- };
- 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)
- : thread_task_runner_handle_override_(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();
- ProcessTasksNoLaterThan(delta);
- ForwardClocksUntilTickTime(original_now_ticks + delta);
- }
- void TestMockTimeTaskRunner::AdvanceMockTickClock(TimeDelta delta) {
- ForwardClocksUntilTickTime(NowTicks() + delta);
- }
- void TestMockTimeTaskRunner::AdvanceWallClock(TimeDelta delta) {
- now_ += delta;
- OnAfterTimePassed();
- }
- void TestMockTimeTaskRunner::RunUntilIdle() {
- DCHECK(thread_checker_.CalledOnValidThread());
- ProcessTasksNoLaterThan(TimeDelta());
- }
- void TestMockTimeTaskRunner::ProcessNextNTasks(int n) {
- DCHECK(thread_checker_.CalledOnValidThread());
- ProcessTasksNoLaterThan(TimeDelta::Max(), n);
- }
- void TestMockTimeTaskRunner::FastForwardUntilNoTasksRemain() {
- DCHECK(thread_checker_.CalledOnValidThread());
- ProcessTasksNoLaterThan(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_;
- }
- Clock* TestMockTimeTaskRunner::GetMockClock() const {
- DCHECK(thread_checker_.CalledOnValidThread());
- return &mock_clock_;
- }
- 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_;
- }
- void TestMockTimeTaskRunner::DetachFromThread() {
- thread_checker_.DetachFromThread();
- }
- // 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::PostDelayedTaskAt(
- subtle::PostDelayedTaskPassKey,
- const Location& from_here,
- OnceClosure task,
- TimeTicks delayed_run_time,
- subtle::DelayPolicy deadline_policy) {
- return PostDelayedTask(
- from_here, std::move(task),
- delayed_run_time.is_null() ? TimeDelta() : delayed_run_time - now_ticks_);
- }
- 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::ProcessTasksNoLaterThan(TimeDelta max_delta,
- int limit) {
- 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.
- absl::optional<ThreadTaskRunnerHandleOverrideForTesting> ttrh_override;
- if (!ThreadTaskRunnerHandle::IsSet() ||
- ThreadTaskRunnerHandle::Get() != proxy_task_runner_.get()) {
- ttrh_override.emplace(proxy_task_runner_.get());
- }
- const TimeTicks original_now_ticks = NowTicks();
- for (int i = 0; !quit_run_loop_ && (limit < 0 || i < limit); i++) {
- 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,
- TimeDelta timeout) {
- 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.";
- // This computation relies on saturated arithmetic.
- TimeTicks run_until = now_ticks_ + timeout;
- while (!quit_run_loop_ && now_ticks_ < run_until) {
- 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 =
- std::min(run_until, 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
|