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