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