1// Copyright 2014 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 "cc/test/ordered_simple_task_runner.h"
6
7#include <limits>
8#include <set>
9#include <sstream>
10#include <string>
11#include <vector>
12
13#include "base/auto_reset.h"
14#include "base/debug/trace_event.h"
15#include "base/debug/trace_event_argument.h"
16#include "base/strings/string_number_conversions.h"
17
18#define TRACE_TASK(function, task) \
19  TRACE_EVENT_INSTANT1(            \
20      "cc", function, TRACE_EVENT_SCOPE_THREAD, "task", task.AsValue());
21
22#define TRACE_TASK_RUN(function, tag, task)
23
24namespace cc {
25
26// TestOrderablePendingTask implementation
27TestOrderablePendingTask::TestOrderablePendingTask()
28    : base::TestPendingTask(),
29      task_id_(TestOrderablePendingTask::task_id_counter++) {
30}
31
32TestOrderablePendingTask::TestOrderablePendingTask(
33    const tracked_objects::Location& location,
34    const base::Closure& task,
35    base::TimeTicks post_time,
36    base::TimeDelta delay,
37    TestNestability nestability)
38    : base::TestPendingTask(location, task, post_time, delay, nestability),
39      task_id_(TestOrderablePendingTask::task_id_counter++) {
40}
41
42size_t TestOrderablePendingTask::task_id_counter = 0;
43
44TestOrderablePendingTask::~TestOrderablePendingTask() {
45}
46
47bool TestOrderablePendingTask::operator==(
48    const TestOrderablePendingTask& other) const {
49  return task_id_ == other.task_id_;
50}
51
52bool TestOrderablePendingTask::operator<(
53    const TestOrderablePendingTask& other) const {
54  if (*this == other)
55    return false;
56
57  if (GetTimeToRun() == other.GetTimeToRun()) {
58    return task_id_ < other.task_id_;
59  }
60  return ShouldRunBefore(other);
61}
62
63scoped_refptr<base::debug::ConvertableToTraceFormat>
64TestOrderablePendingTask::AsValue() const {
65  scoped_refptr<base::debug::TracedValue> state =
66      new base::debug::TracedValue();
67  AsValueInto(state.get());
68  return state;
69}
70
71void TestOrderablePendingTask::AsValueInto(
72    base::debug::TracedValue* state) const {
73  state->SetInteger("id", task_id_);
74  state->SetInteger("run_at", GetTimeToRun().ToInternalValue());
75  state->SetString("posted_from", location.ToString());
76}
77
78OrderedSimpleTaskRunner::OrderedSimpleTaskRunner()
79    : advance_now_(true),
80      now_src_(TestNowSource::Create(0)),
81      inside_run_tasks_until_(false) {
82}
83
84OrderedSimpleTaskRunner::OrderedSimpleTaskRunner(
85    scoped_refptr<TestNowSource> now_src,
86    bool advance_now)
87    : advance_now_(advance_now),
88      now_src_(now_src),
89      max_tasks_(kAbsoluteMaxTasks),
90      inside_run_tasks_until_(false) {
91}
92
93OrderedSimpleTaskRunner::~OrderedSimpleTaskRunner() {}
94
95// base::TestSimpleTaskRunner implementation
96bool OrderedSimpleTaskRunner::PostDelayedTask(
97    const tracked_objects::Location& from_here,
98    const base::Closure& task,
99    base::TimeDelta delay) {
100  DCHECK(thread_checker_.CalledOnValidThread());
101  TestOrderablePendingTask pt(
102      from_here, task, now_src_->Now(), delay, base::TestPendingTask::NESTABLE);
103
104  TRACE_TASK("OrderedSimpleTaskRunner::PostDelayedTask", pt);
105  pending_tasks_.insert(pt);
106  return true;
107}
108
109bool OrderedSimpleTaskRunner::PostNonNestableDelayedTask(
110    const tracked_objects::Location& from_here,
111    const base::Closure& task,
112    base::TimeDelta delay) {
113  DCHECK(thread_checker_.CalledOnValidThread());
114  TestOrderablePendingTask pt(from_here,
115                              task,
116                              now_src_->Now(),
117                              delay,
118                              base::TestPendingTask::NON_NESTABLE);
119
120  TRACE_TASK("OrderedSimpleTaskRunner::PostNonNestableDelayedTask", pt);
121  pending_tasks_.insert(pt);
122  return true;
123}
124
125bool OrderedSimpleTaskRunner::RunsTasksOnCurrentThread() const {
126  DCHECK(thread_checker_.CalledOnValidThread());
127  return true;
128}
129
130base::TimeTicks OrderedSimpleTaskRunner::NextTaskTime() {
131  if (pending_tasks_.size() <= 0) {
132    return TestNowSource::kAbsoluteMaxNow;
133  }
134
135  return pending_tasks_.begin()->GetTimeToRun();
136}
137
138base::TimeDelta OrderedSimpleTaskRunner::DelayToNextTaskTime() {
139  DCHECK(thread_checker_.CalledOnValidThread());
140
141  if (pending_tasks_.size() <= 0) {
142    return TestNowSource::kAbsoluteMaxNow - base::TimeTicks();
143  }
144
145  base::TimeDelta delay = NextTaskTime() - now_src_->Now();
146  if (delay > base::TimeDelta())
147    return delay;
148  return base::TimeDelta();
149}
150
151const size_t OrderedSimpleTaskRunner::kAbsoluteMaxTasks =
152    std::numeric_limits<size_t>::max();
153
154bool OrderedSimpleTaskRunner::RunTasksWhile(
155    base::Callback<bool(void)> condition) {
156  std::vector<base::Callback<bool(void)> > conditions(1);
157  conditions[0] = condition;
158  return RunTasksWhile(conditions);
159}
160
161bool OrderedSimpleTaskRunner::RunTasksWhile(
162    const std::vector<base::Callback<bool(void)> >& conditions) {
163  TRACE_EVENT2("cc",
164               "OrderedSimpleTaskRunner::RunPendingTasks",
165               "this",
166               AsValue(),
167               "nested",
168               inside_run_tasks_until_);
169  DCHECK(thread_checker_.CalledOnValidThread());
170
171  if (inside_run_tasks_until_)
172    return true;
173
174  base::AutoReset<bool> reset_inside_run_tasks_until_(&inside_run_tasks_until_,
175                                                      true);
176
177  // Make a copy so we can append some extra run checks.
178  std::vector<base::Callback<bool(void)> > modifiable_conditions(conditions);
179
180  // Provide a timeout base on number of tasks run so this doesn't loop
181  // forever.
182  modifiable_conditions.push_back(TaskRunCountBelow(max_tasks_));
183
184  // If to advance now or not
185  if (!advance_now_) {
186    modifiable_conditions.push_back(NowBefore(now_src_->Now()));
187  } else {
188    modifiable_conditions.push_back(AdvanceNow());
189  }
190
191  while (pending_tasks_.size() > 0) {
192    // Check if we should continue to run pending tasks.
193    bool condition_success = true;
194    for (std::vector<base::Callback<bool(void)> >::iterator it =
195             modifiable_conditions.begin();
196         it != modifiable_conditions.end();
197         it++) {
198      condition_success = it->Run();
199      if (!condition_success)
200        break;
201    }
202
203    // Conditions could modify the pending task length, so we need to recheck
204    // that there are tasks to run.
205    if (!condition_success || pending_tasks_.size() == 0) {
206      break;
207    }
208
209    std::set<TestOrderablePendingTask>::iterator task_to_run =
210        pending_tasks_.begin();
211    {
212      TRACE_EVENT1("cc",
213                   "OrderedSimpleTaskRunner::RunPendingTasks running",
214                   "task",
215                   task_to_run->AsValue());
216      task_to_run->task.Run();
217    }
218
219    pending_tasks_.erase(task_to_run);
220  }
221
222  return pending_tasks_.size() > 0;
223}
224
225bool OrderedSimpleTaskRunner::RunPendingTasks() {
226  return RunTasksWhile(TaskExistedInitially());
227}
228
229bool OrderedSimpleTaskRunner::RunUntilIdle() {
230  return RunTasksWhile(std::vector<base::Callback<bool(void)> >());
231}
232
233bool OrderedSimpleTaskRunner::RunUntilTime(base::TimeTicks time) {
234  // If we are not auto advancing, force now forward to the time.
235  if (!advance_now_ && now_src_->Now() < time)
236    now_src_->SetNow(time);
237
238  // Run tasks
239  bool result = RunTasksWhile(NowBefore(time));
240
241  // If the next task is after the stopping time and auto-advancing now, then
242  // force time to be the stopping time.
243  if (!result && advance_now_ && now_src_->Now() < time) {
244    now_src_->SetNow(time);
245  }
246
247  return result;
248}
249
250bool OrderedSimpleTaskRunner::RunForPeriod(base::TimeDelta period) {
251  return RunUntilTime(now_src_->Now() + period);
252}
253
254// base::debug tracing functionality
255scoped_refptr<base::debug::ConvertableToTraceFormat>
256OrderedSimpleTaskRunner::AsValue() const {
257  scoped_refptr<base::debug::TracedValue> state =
258      new base::debug::TracedValue();
259  AsValueInto(state.get());
260  return state;
261}
262
263void OrderedSimpleTaskRunner::AsValueInto(
264    base::debug::TracedValue* state) const {
265  state->SetInteger("pending_tasks", pending_tasks_.size());
266  for (std::set<TestOrderablePendingTask>::const_iterator it =
267           pending_tasks_.begin();
268       it != pending_tasks_.end();
269       ++it) {
270    state->BeginDictionary(
271        base::SizeTToString(std::distance(pending_tasks_.begin(), it)).c_str());
272    it->AsValueInto(state);
273    state->EndDictionary();
274  }
275  now_src_->AsValueInto(state);
276}
277
278base::Callback<bool(void)> OrderedSimpleTaskRunner::TaskRunCountBelow(
279    size_t max_tasks) {
280  return base::Bind(&OrderedSimpleTaskRunner::TaskRunCountBelowCallback,
281                    max_tasks,
282                    base::Owned(new size_t(0)));
283}
284
285bool OrderedSimpleTaskRunner::TaskRunCountBelowCallback(size_t max_tasks,
286                                                        size_t* tasks_run) {
287  return (*tasks_run)++ < max_tasks;
288}
289
290base::Callback<bool(void)> OrderedSimpleTaskRunner::TaskExistedInitially() {
291  // base::Bind takes a copy of pending_tasks_
292  return base::Bind(&OrderedSimpleTaskRunner::TaskExistedInitiallyCallback,
293                    base::Unretained(this),
294                    pending_tasks_);
295}
296
297bool OrderedSimpleTaskRunner::TaskExistedInitiallyCallback(
298    const std::set<TestOrderablePendingTask>& existing_tasks) {
299  return existing_tasks.find(*pending_tasks_.begin()) != existing_tasks.end();
300}
301
302base::Callback<bool(void)> OrderedSimpleTaskRunner::NowBefore(
303    base::TimeTicks stop_at) {
304  return base::Bind(&OrderedSimpleTaskRunner::NowBeforeCallback,
305                    base::Unretained(this),
306                    stop_at);
307}
308bool OrderedSimpleTaskRunner::NowBeforeCallback(base::TimeTicks stop_at) {
309  return NextTaskTime() <= stop_at;
310}
311
312base::Callback<bool(void)> OrderedSimpleTaskRunner::AdvanceNow() {
313  return base::Bind(&OrderedSimpleTaskRunner::AdvanceNowCallback,
314                    base::Unretained(this));
315}
316
317bool OrderedSimpleTaskRunner::AdvanceNowCallback() {
318  base::TimeTicks next_task_time = NextTaskTime();
319  if (now_src_->Now() < next_task_time) {
320    now_src_->SetNow(next_task_time);
321  }
322  return true;
323}
324
325}  // namespace cc
326