1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/test/test_mock_time_task_runner.h"
6
7 #include <utility>
8
9 #include "base/containers/circular_deque.h"
10 #include "base/logging.h"
11 #include "base/macros.h"
12 #include "base/memory/ptr_util.h"
13 #include "base/memory/ref_counted.h"
14 #include "base/threading/thread_task_runner_handle.h"
15
16 namespace base {
17 namespace {
18
19 // LegacyMockTickClock and LegacyMockClock are used by deprecated APIs of
20 // TestMockTimeTaskRunner. They will be removed after updating callers of
21 // GetMockClock() and GetMockTickClock() to GetMockClockPtr() and
22 // GetMockTickClockPtr().
23 class LegacyMockTickClock : public TickClock {
24 public:
LegacyMockTickClock(scoped_refptr<const TestMockTimeTaskRunner> task_runner)25 explicit LegacyMockTickClock(
26 scoped_refptr<const TestMockTimeTaskRunner> task_runner)
27 : task_runner_(std::move(task_runner)) {}
28
29 // TickClock:
NowTicks() const30 TimeTicks NowTicks() const override { return task_runner_->NowTicks(); }
31
32 private:
33 scoped_refptr<const TestMockTimeTaskRunner> task_runner_;
34
35 DISALLOW_COPY_AND_ASSIGN(LegacyMockTickClock);
36 };
37
38 class LegacyMockClock : public Clock {
39 public:
LegacyMockClock(scoped_refptr<const TestMockTimeTaskRunner> task_runner)40 explicit LegacyMockClock(
41 scoped_refptr<const TestMockTimeTaskRunner> task_runner)
42 : task_runner_(std::move(task_runner)) {}
43
44 // Clock:
Now() const45 Time Now() const override { return task_runner_->Now(); }
46
47 private:
48 scoped_refptr<const TestMockTimeTaskRunner> task_runner_;
49
50 DISALLOW_COPY_AND_ASSIGN(LegacyMockClock);
51 };
52
53 } // namespace
54
55 // A SingleThreadTaskRunner which forwards everything to its |target_|. This
56 // serves two purposes:
57 // 1) If a ThreadTaskRunnerHandle owned by TestMockTimeTaskRunner were to be
58 // set to point to that TestMockTimeTaskRunner, a reference cycle would
59 // result. As |target_| here is a non-refcounting raw pointer, the cycle is
60 // broken.
61 // 2) Since SingleThreadTaskRunner is ref-counted, it's quite easy for it to
62 // accidentally get captured between tests in a singleton somewhere.
63 // Indirecting via NonOwningProxyTaskRunner permits TestMockTimeTaskRunner
64 // to be cleaned up (removing the RunLoop::Delegate in the kBoundToThread
65 // mode), and to also cleanly flag any actual attempts to use the leaked
66 // task runner.
67 class TestMockTimeTaskRunner::NonOwningProxyTaskRunner
68 : public SingleThreadTaskRunner {
69 public:
NonOwningProxyTaskRunner(SingleThreadTaskRunner * target)70 explicit NonOwningProxyTaskRunner(SingleThreadTaskRunner* target)
71 : target_(target) {
72 DCHECK(target_);
73 }
74
75 // Detaches this NonOwningProxyTaskRunner instance from its |target_|. It is
76 // invalid to post tasks after this point but RunsTasksInCurrentSequence()
77 // will still pass on the original thread for convenience with legacy code.
Detach()78 void Detach() {
79 AutoLock scoped_lock(lock_);
80 target_ = nullptr;
81 }
82
83 // SingleThreadTaskRunner:
RunsTasksInCurrentSequence() const84 bool RunsTasksInCurrentSequence() const override {
85 AutoLock scoped_lock(lock_);
86 if (target_)
87 return target_->RunsTasksInCurrentSequence();
88 return thread_checker_.CalledOnValidThread();
89 }
90
PostDelayedTask(const Location & from_here,OnceClosure task,TimeDelta delay)91 bool PostDelayedTask(const Location& from_here,
92 OnceClosure task,
93 TimeDelta delay) override {
94 AutoLock scoped_lock(lock_);
95 if (target_)
96 return target_->PostDelayedTask(from_here, std::move(task), delay);
97
98 // The associated TestMockTimeTaskRunner is dead, so fail this PostTask.
99 return false;
100 }
101
PostNonNestableDelayedTask(const Location & from_here,OnceClosure task,TimeDelta delay)102 bool PostNonNestableDelayedTask(const Location& from_here,
103 OnceClosure task,
104 TimeDelta delay) override {
105 AutoLock scoped_lock(lock_);
106 if (target_) {
107 return target_->PostNonNestableDelayedTask(from_here, std::move(task),
108 delay);
109 }
110
111 // The associated TestMockTimeTaskRunner is dead, so fail this PostTask.
112 return false;
113 }
114
115 private:
116 friend class RefCountedThreadSafe<NonOwningProxyTaskRunner>;
117 ~NonOwningProxyTaskRunner() override = default;
118
119 mutable Lock lock_;
120 SingleThreadTaskRunner* target_; // guarded by lock_
121
122 // Used to implement RunsTasksInCurrentSequence, without relying on |target_|.
123 ThreadCheckerImpl thread_checker_;
124
125 DISALLOW_COPY_AND_ASSIGN(NonOwningProxyTaskRunner);
126 };
127
128 // TestMockTimeTaskRunner::TestOrderedPendingTask -----------------------------
129
130 // Subclass of TestPendingTask which has a strictly monotonically increasing ID
131 // for every task, so that tasks posted with the same 'time to run' can be run
132 // in the order of being posted.
133 struct TestMockTimeTaskRunner::TestOrderedPendingTask
134 : public base::TestPendingTask {
135 TestOrderedPendingTask();
136 TestOrderedPendingTask(const Location& location,
137 OnceClosure task,
138 TimeTicks post_time,
139 TimeDelta delay,
140 size_t ordinal,
141 TestNestability nestability);
142 TestOrderedPendingTask(TestOrderedPendingTask&&);
143 ~TestOrderedPendingTask();
144
145 TestOrderedPendingTask& operator=(TestOrderedPendingTask&&);
146
147 size_t ordinal;
148
149 private:
150 DISALLOW_COPY_AND_ASSIGN(TestOrderedPendingTask);
151 };
152
TestOrderedPendingTask()153 TestMockTimeTaskRunner::TestOrderedPendingTask::TestOrderedPendingTask()
154 : ordinal(0) {
155 }
156
157 TestMockTimeTaskRunner::TestOrderedPendingTask::TestOrderedPendingTask(
158 TestOrderedPendingTask&&) = default;
159
TestOrderedPendingTask(const Location & location,OnceClosure task,TimeTicks post_time,TimeDelta delay,size_t ordinal,TestNestability nestability)160 TestMockTimeTaskRunner::TestOrderedPendingTask::TestOrderedPendingTask(
161 const Location& location,
162 OnceClosure task,
163 TimeTicks post_time,
164 TimeDelta delay,
165 size_t ordinal,
166 TestNestability nestability)
167 : base::TestPendingTask(location,
168 std::move(task),
169 post_time,
170 delay,
171 nestability),
172 ordinal(ordinal) {}
173
174 TestMockTimeTaskRunner::TestOrderedPendingTask::~TestOrderedPendingTask() =
175 default;
176
177 TestMockTimeTaskRunner::TestOrderedPendingTask&
178 TestMockTimeTaskRunner::TestOrderedPendingTask::operator=(
179 TestOrderedPendingTask&&) = default;
180
181 // TestMockTimeTaskRunner -----------------------------------------------------
182
183 // TODO(gab): This should also set the SequenceToken for the current thread.
184 // Ref. TestMockTimeTaskRunner::RunsTasksInCurrentSequence().
ScopedContext(scoped_refptr<TestMockTimeTaskRunner> scope)185 TestMockTimeTaskRunner::ScopedContext::ScopedContext(
186 scoped_refptr<TestMockTimeTaskRunner> scope)
187 : on_destroy_(ThreadTaskRunnerHandle::OverrideForTesting(scope)) {
188 scope->RunUntilIdle();
189 }
190
191 TestMockTimeTaskRunner::ScopedContext::~ScopedContext() = default;
192
operator ()(const TestOrderedPendingTask & first_task,const TestOrderedPendingTask & second_task) const193 bool TestMockTimeTaskRunner::TemporalOrder::operator()(
194 const TestOrderedPendingTask& first_task,
195 const TestOrderedPendingTask& second_task) const {
196 if (first_task.GetTimeToRun() == second_task.GetTimeToRun())
197 return first_task.ordinal > second_task.ordinal;
198 return first_task.GetTimeToRun() > second_task.GetTimeToRun();
199 }
200
TestMockTimeTaskRunner(Type type)201 TestMockTimeTaskRunner::TestMockTimeTaskRunner(Type type)
202 : TestMockTimeTaskRunner(Time::UnixEpoch(), TimeTicks(), type) {}
203
TestMockTimeTaskRunner(Time start_time,TimeTicks start_ticks,Type type)204 TestMockTimeTaskRunner::TestMockTimeTaskRunner(Time start_time,
205 TimeTicks start_ticks,
206 Type type)
207 : now_(start_time),
208 now_ticks_(start_ticks),
209 tasks_lock_cv_(&tasks_lock_),
210 proxy_task_runner_(MakeRefCounted<NonOwningProxyTaskRunner>(this)),
211 mock_clock_(this) {
212 if (type == Type::kBoundToThread) {
213 RunLoop::RegisterDelegateForCurrentThread(this);
214 thread_task_runner_handle_ =
215 std::make_unique<ThreadTaskRunnerHandle>(proxy_task_runner_);
216 }
217 }
218
~TestMockTimeTaskRunner()219 TestMockTimeTaskRunner::~TestMockTimeTaskRunner() {
220 proxy_task_runner_->Detach();
221 }
222
FastForwardBy(TimeDelta delta)223 void TestMockTimeTaskRunner::FastForwardBy(TimeDelta delta) {
224 DCHECK(thread_checker_.CalledOnValidThread());
225 DCHECK_GE(delta, TimeDelta());
226
227 const TimeTicks original_now_ticks = NowTicks();
228 ProcessAllTasksNoLaterThan(delta);
229 ForwardClocksUntilTickTime(original_now_ticks + delta);
230 }
231
AdvanceMockTickClock(TimeDelta delta)232 void TestMockTimeTaskRunner::AdvanceMockTickClock(TimeDelta delta) {
233 ForwardClocksUntilTickTime(NowTicks() + delta);
234 }
235
RunUntilIdle()236 void TestMockTimeTaskRunner::RunUntilIdle() {
237 DCHECK(thread_checker_.CalledOnValidThread());
238 ProcessAllTasksNoLaterThan(TimeDelta());
239 }
240
FastForwardUntilNoTasksRemain()241 void TestMockTimeTaskRunner::FastForwardUntilNoTasksRemain() {
242 DCHECK(thread_checker_.CalledOnValidThread());
243 ProcessAllTasksNoLaterThan(TimeDelta::Max());
244 }
245
ClearPendingTasks()246 void TestMockTimeTaskRunner::ClearPendingTasks() {
247 AutoLock scoped_lock(tasks_lock_);
248 // This is repeated in case task destruction triggers further tasks.
249 while (!tasks_.empty()) {
250 TaskPriorityQueue cleanup_tasks;
251 tasks_.swap(cleanup_tasks);
252
253 // Destroy task objects with |tasks_lock_| released. Task deletion can cause
254 // calls to NonOwningProxyTaskRunner::RunsTasksInCurrentSequence()
255 // (e.g. for DCHECKs), which causes |NonOwningProxyTaskRunner::lock_| to be
256 // grabbed.
257 //
258 // On the other hand, calls from NonOwningProxyTaskRunner::PostTask ->
259 // TestMockTimeTaskRunner::PostTask acquire locks as
260 // |NonOwningProxyTaskRunner::lock_| followed by |tasks_lock_|, so it's
261 // desirable to avoid the reverse order, for deadlock freedom.
262 AutoUnlock scoped_unlock(tasks_lock_);
263 while (!cleanup_tasks.empty())
264 cleanup_tasks.pop();
265 }
266 }
267
Now() const268 Time TestMockTimeTaskRunner::Now() const {
269 AutoLock scoped_lock(tasks_lock_);
270 return now_;
271 }
272
NowTicks() const273 TimeTicks TestMockTimeTaskRunner::NowTicks() const {
274 AutoLock scoped_lock(tasks_lock_);
275 return now_ticks_;
276 }
277
DeprecatedGetMockClock() const278 std::unique_ptr<Clock> TestMockTimeTaskRunner::DeprecatedGetMockClock() const {
279 DCHECK(thread_checker_.CalledOnValidThread());
280 return std::make_unique<LegacyMockClock>(this);
281 }
282
GetMockClock() const283 Clock* TestMockTimeTaskRunner::GetMockClock() const {
284 DCHECK(thread_checker_.CalledOnValidThread());
285 return &mock_clock_;
286 }
287
DeprecatedGetMockTickClock() const288 std::unique_ptr<TickClock> TestMockTimeTaskRunner::DeprecatedGetMockTickClock()
289 const {
290 DCHECK(thread_checker_.CalledOnValidThread());
291 return std::make_unique<LegacyMockTickClock>(this);
292 }
293
GetMockTickClock() const294 const TickClock* TestMockTimeTaskRunner::GetMockTickClock() const {
295 DCHECK(thread_checker_.CalledOnValidThread());
296 return &mock_clock_;
297 }
298
299 base::circular_deque<TestPendingTask>
TakePendingTasks()300 TestMockTimeTaskRunner::TakePendingTasks() {
301 AutoLock scoped_lock(tasks_lock_);
302 base::circular_deque<TestPendingTask> tasks;
303 while (!tasks_.empty()) {
304 // It's safe to remove const and consume |task| here, since |task| is not
305 // used for ordering the item.
306 if (!tasks_.top().task.IsCancelled()) {
307 tasks.push_back(
308 std::move(const_cast<TestOrderedPendingTask&>(tasks_.top())));
309 }
310 tasks_.pop();
311 }
312 return tasks;
313 }
314
HasPendingTask()315 bool TestMockTimeTaskRunner::HasPendingTask() {
316 DCHECK(thread_checker_.CalledOnValidThread());
317 AutoLock scoped_lock(tasks_lock_);
318 while (!tasks_.empty() && tasks_.top().task.IsCancelled())
319 tasks_.pop();
320 return !tasks_.empty();
321 }
322
GetPendingTaskCount()323 size_t TestMockTimeTaskRunner::GetPendingTaskCount() {
324 DCHECK(thread_checker_.CalledOnValidThread());
325 AutoLock scoped_lock(tasks_lock_);
326 TaskPriorityQueue preserved_tasks;
327 while (!tasks_.empty()) {
328 if (!tasks_.top().task.IsCancelled()) {
329 preserved_tasks.push(
330 std::move(const_cast<TestOrderedPendingTask&>(tasks_.top())));
331 }
332 tasks_.pop();
333 }
334 tasks_.swap(preserved_tasks);
335 return tasks_.size();
336 }
337
NextPendingTaskDelay()338 TimeDelta TestMockTimeTaskRunner::NextPendingTaskDelay() {
339 DCHECK(thread_checker_.CalledOnValidThread());
340 AutoLock scoped_lock(tasks_lock_);
341 while (!tasks_.empty() && tasks_.top().task.IsCancelled())
342 tasks_.pop();
343 return tasks_.empty() ? TimeDelta::Max()
344 : tasks_.top().GetTimeToRun() - now_ticks_;
345 }
346
347 // TODO(gab): Combine |thread_checker_| with a SequenceToken to differentiate
348 // between tasks running in the scope of this TestMockTimeTaskRunner and other
349 // task runners sharing this thread. http://crbug.com/631186
RunsTasksInCurrentSequence() const350 bool TestMockTimeTaskRunner::RunsTasksInCurrentSequence() const {
351 return thread_checker_.CalledOnValidThread();
352 }
353
PostDelayedTask(const Location & from_here,OnceClosure task,TimeDelta delay)354 bool TestMockTimeTaskRunner::PostDelayedTask(const Location& from_here,
355 OnceClosure task,
356 TimeDelta delay) {
357 AutoLock scoped_lock(tasks_lock_);
358 tasks_.push(TestOrderedPendingTask(from_here, std::move(task), now_ticks_,
359 delay, next_task_ordinal_++,
360 TestPendingTask::NESTABLE));
361 tasks_lock_cv_.Signal();
362 return true;
363 }
364
PostNonNestableDelayedTask(const Location & from_here,OnceClosure task,TimeDelta delay)365 bool TestMockTimeTaskRunner::PostNonNestableDelayedTask(
366 const Location& from_here,
367 OnceClosure task,
368 TimeDelta delay) {
369 return PostDelayedTask(from_here, std::move(task), delay);
370 }
371
OnBeforeSelectingTask()372 void TestMockTimeTaskRunner::OnBeforeSelectingTask() {
373 // Empty default implementation.
374 }
375
OnAfterTimePassed()376 void TestMockTimeTaskRunner::OnAfterTimePassed() {
377 // Empty default implementation.
378 }
379
OnAfterTaskRun()380 void TestMockTimeTaskRunner::OnAfterTaskRun() {
381 // Empty default implementation.
382 }
383
ProcessAllTasksNoLaterThan(TimeDelta max_delta)384 void TestMockTimeTaskRunner::ProcessAllTasksNoLaterThan(TimeDelta max_delta) {
385 DCHECK(thread_checker_.CalledOnValidThread());
386 DCHECK_GE(max_delta, TimeDelta());
387
388 // Multiple test task runners can share the same thread for determinism in
389 // unit tests. Make sure this TestMockTimeTaskRunner's tasks run in its scope.
390 ScopedClosureRunner undo_override;
391 if (!ThreadTaskRunnerHandle::IsSet() ||
392 ThreadTaskRunnerHandle::Get() != proxy_task_runner_.get()) {
393 undo_override =
394 ThreadTaskRunnerHandle::OverrideForTesting(proxy_task_runner_.get());
395 }
396
397 const TimeTicks original_now_ticks = NowTicks();
398 while (!quit_run_loop_) {
399 OnBeforeSelectingTask();
400 TestPendingTask task_info;
401 if (!DequeueNextTask(original_now_ticks, max_delta, &task_info))
402 break;
403 if (task_info.task.IsCancelled())
404 continue;
405 // If tasks were posted with a negative delay, task_info.GetTimeToRun() will
406 // be less than |now_ticks_|. ForwardClocksUntilTickTime() takes care of not
407 // moving the clock backwards in this case.
408 ForwardClocksUntilTickTime(task_info.GetTimeToRun());
409 std::move(task_info.task).Run();
410 OnAfterTaskRun();
411 }
412 }
413
ForwardClocksUntilTickTime(TimeTicks later_ticks)414 void TestMockTimeTaskRunner::ForwardClocksUntilTickTime(TimeTicks later_ticks) {
415 DCHECK(thread_checker_.CalledOnValidThread());
416 {
417 AutoLock scoped_lock(tasks_lock_);
418 if (later_ticks <= now_ticks_)
419 return;
420
421 now_ += later_ticks - now_ticks_;
422 now_ticks_ = later_ticks;
423 }
424 OnAfterTimePassed();
425 }
426
DequeueNextTask(const TimeTicks & reference,const TimeDelta & max_delta,TestPendingTask * next_task)427 bool TestMockTimeTaskRunner::DequeueNextTask(const TimeTicks& reference,
428 const TimeDelta& max_delta,
429 TestPendingTask* next_task) {
430 DCHECK(thread_checker_.CalledOnValidThread());
431 AutoLock scoped_lock(tasks_lock_);
432 if (!tasks_.empty() &&
433 (tasks_.top().GetTimeToRun() - reference) <= max_delta) {
434 // It's safe to remove const and consume |task| here, since |task| is not
435 // used for ordering the item.
436 *next_task = std::move(const_cast<TestOrderedPendingTask&>(tasks_.top()));
437 tasks_.pop();
438 return true;
439 }
440 return false;
441 }
442
Run(bool application_tasks_allowed)443 void TestMockTimeTaskRunner::Run(bool application_tasks_allowed) {
444 DCHECK(thread_checker_.CalledOnValidThread());
445
446 // Since TestMockTimeTaskRunner doesn't process system messages: there's no
447 // hope for anything but an application task to call Quit(). If this RunLoop
448 // can't process application tasks (i.e. disallowed by default in nested
449 // RunLoops) it's guaranteed to hang...
450 DCHECK(application_tasks_allowed)
451 << "This is a nested RunLoop instance and needs to be of "
452 "Type::kNestableTasksAllowed.";
453
454 while (!quit_run_loop_) {
455 RunUntilIdle();
456 if (quit_run_loop_ || ShouldQuitWhenIdle())
457 break;
458
459 // Peek into |tasks_| to perform one of two things:
460 // A) If there are no remaining tasks, wait until one is posted and
461 // restart from the top.
462 // B) If there is a remaining delayed task. Fast-forward to reach the next
463 // round of tasks.
464 TimeDelta auto_fast_forward_by;
465 {
466 AutoLock scoped_lock(tasks_lock_);
467 if (tasks_.empty()) {
468 while (tasks_.empty())
469 tasks_lock_cv_.Wait();
470 continue;
471 }
472 auto_fast_forward_by = tasks_.top().GetTimeToRun() - now_ticks_;
473 }
474 FastForwardBy(auto_fast_forward_by);
475 }
476 quit_run_loop_ = false;
477 }
478
Quit()479 void TestMockTimeTaskRunner::Quit() {
480 DCHECK(thread_checker_.CalledOnValidThread());
481 quit_run_loop_ = true;
482 }
483
EnsureWorkScheduled()484 void TestMockTimeTaskRunner::EnsureWorkScheduled() {
485 // Nothing to do: TestMockTimeTaskRunner::Run() will always process tasks and
486 // doesn't need an extra kick on nested runs.
487 }
488
NowTicks() const489 TimeTicks TestMockTimeTaskRunner::MockClock::NowTicks() const {
490 return task_runner_->NowTicks();
491 }
492
Now() const493 Time TestMockTimeTaskRunner::MockClock::Now() const {
494 return task_runner_->Now();
495 }
496
497 } // namespace base
498