• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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