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