// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "cc/test/ordered_simple_task_runner.h" #include #include #include #include #include #include "base/auto_reset.h" #include "base/numerics/safe_conversions.h" #include "base/strings/string_number_conversions.h" #include "base/trace_event/trace_event.h" #include "base/trace_event/trace_event_argument.h" #define TRACE_TASK(function, task) \ TRACE_EVENT_INSTANT1( \ "cc", function, TRACE_EVENT_SCOPE_THREAD, "task", task.AsValue()); #define TRACE_TASK_RUN(function, tag, task) namespace cc { // TestOrderablePendingTask implementation TestOrderablePendingTask::TestOrderablePendingTask() : base::TestPendingTask(), task_id_(TestOrderablePendingTask::task_id_counter++) { } TestOrderablePendingTask::TestOrderablePendingTask( const tracked_objects::Location& location, const base::Closure& task, base::TimeTicks post_time, base::TimeDelta delay, TestNestability nestability) : base::TestPendingTask(location, task, post_time, delay, nestability), task_id_(TestOrderablePendingTask::task_id_counter++) { } size_t TestOrderablePendingTask::task_id_counter = 0; TestOrderablePendingTask::~TestOrderablePendingTask() { } bool TestOrderablePendingTask::operator==( const TestOrderablePendingTask& other) const { return task_id_ == other.task_id_; } bool TestOrderablePendingTask::operator<( const TestOrderablePendingTask& other) const { if (*this == other) return false; if (GetTimeToRun() == other.GetTimeToRun()) { return task_id_ < other.task_id_; } return ShouldRunBefore(other); } scoped_refptr TestOrderablePendingTask::AsValue() const { scoped_refptr state = new base::trace_event::TracedValue(); AsValueInto(state.get()); return state; } void TestOrderablePendingTask::AsValueInto( base::trace_event::TracedValue* state) const { state->SetInteger("id", base::saturated_cast(task_id_)); state->SetInteger("run_at", GetTimeToRun().ToInternalValue()); state->SetString("posted_from", location.ToString()); } OrderedSimpleTaskRunner::OrderedSimpleTaskRunner( base::SimpleTestTickClock* now_src, bool advance_now) : advance_now_(advance_now), now_src_(now_src), max_tasks_(kAbsoluteMaxTasks), inside_run_tasks_until_(false) { } OrderedSimpleTaskRunner::~OrderedSimpleTaskRunner() {} // static base::TimeTicks OrderedSimpleTaskRunner::AbsoluteMaxNow() { return base::TimeTicks::FromInternalValue( std::numeric_limits::max()); } // base::TestSimpleTaskRunner implementation bool OrderedSimpleTaskRunner::PostDelayedTask( const tracked_objects::Location& from_here, const base::Closure& task, base::TimeDelta delay) { DCHECK(thread_checker_.CalledOnValidThread()); TestOrderablePendingTask pt(from_here, task, now_src_->NowTicks(), delay, base::TestPendingTask::NESTABLE); TRACE_TASK("OrderedSimpleTaskRunner::PostDelayedTask", pt); pending_tasks_.insert(pt); return true; } bool OrderedSimpleTaskRunner::PostNonNestableDelayedTask( const tracked_objects::Location& from_here, const base::Closure& task, base::TimeDelta delay) { DCHECK(thread_checker_.CalledOnValidThread()); TestOrderablePendingTask pt(from_here, task, now_src_->NowTicks(), delay, base::TestPendingTask::NON_NESTABLE); TRACE_TASK("OrderedSimpleTaskRunner::PostNonNestableDelayedTask", pt); pending_tasks_.insert(pt); return true; } bool OrderedSimpleTaskRunner::RunsTasksOnCurrentThread() const { DCHECK(thread_checker_.CalledOnValidThread()); return true; } size_t OrderedSimpleTaskRunner::NumPendingTasks() const { return pending_tasks_.size(); } bool OrderedSimpleTaskRunner::HasPendingTasks() const { return pending_tasks_.size() > 0; } base::TimeTicks OrderedSimpleTaskRunner::NextTaskTime() { if (pending_tasks_.size() <= 0) { return AbsoluteMaxNow(); } return pending_tasks_.begin()->GetTimeToRun(); } base::TimeDelta OrderedSimpleTaskRunner::DelayToNextTaskTime() { DCHECK(thread_checker_.CalledOnValidThread()); if (pending_tasks_.size() <= 0) { return AbsoluteMaxNow() - base::TimeTicks(); } base::TimeDelta delay = NextTaskTime() - now_src_->NowTicks(); if (delay > base::TimeDelta()) return delay; return base::TimeDelta(); } const size_t OrderedSimpleTaskRunner::kAbsoluteMaxTasks = std::numeric_limits::max(); bool OrderedSimpleTaskRunner::RunTasksWhile( base::Callback condition) { std::vector> conditions(1); conditions[0] = condition; return RunTasksWhile(conditions); } bool OrderedSimpleTaskRunner::RunTasksWhile( const std::vector>& conditions) { TRACE_EVENT2("cc", "OrderedSimpleTaskRunner::RunPendingTasks", "this", AsValue(), "nested", inside_run_tasks_until_); DCHECK(thread_checker_.CalledOnValidThread()); if (inside_run_tasks_until_) return true; base::AutoReset reset_inside_run_tasks_until_(&inside_run_tasks_until_, true); // Make a copy so we can append some extra run checks. std::vector> modifiable_conditions(conditions); // Provide a timeout base on number of tasks run so this doesn't loop // forever. modifiable_conditions.push_back(TaskRunCountBelow(max_tasks_)); // If to advance now or not if (!advance_now_) { modifiable_conditions.push_back(NowBefore(now_src_->NowTicks())); } else { modifiable_conditions.push_back(AdvanceNow()); } while (pending_tasks_.size() > 0) { // Check if we should continue to run pending tasks. bool condition_success = true; for (std::vector>::iterator it = modifiable_conditions.begin(); it != modifiable_conditions.end(); it++) { condition_success = it->Run(); if (!condition_success) break; } // Conditions could modify the pending task length, so we need to recheck // that there are tasks to run. if (!condition_success || !HasPendingTasks()) { break; } std::set::iterator task_to_run = pending_tasks_.begin(); { TRACE_EVENT1("cc", "OrderedSimpleTaskRunner::RunPendingTasks running", "task", task_to_run->AsValue()); task_to_run->task.Run(); } pending_tasks_.erase(task_to_run); } return HasPendingTasks(); } bool OrderedSimpleTaskRunner::RunPendingTasks() { return RunTasksWhile(TaskExistedInitially()); } bool OrderedSimpleTaskRunner::RunUntilIdle() { return RunTasksWhile(std::vector>()); } bool OrderedSimpleTaskRunner::RunUntilTime(base::TimeTicks time) { // If we are not auto advancing, force now forward to the time. if (!advance_now_ && now_src_->NowTicks() < time) now_src_->Advance(time - now_src_->NowTicks()); // Run tasks bool result = RunTasksWhile(NowBefore(time)); // If the next task is after the stopping time and auto-advancing now, then // force time to be the stopping time. if (!result && advance_now_ && now_src_->NowTicks() < time) { now_src_->Advance(time - now_src_->NowTicks()); } return result; } bool OrderedSimpleTaskRunner::RunForPeriod(base::TimeDelta period) { return RunUntilTime(now_src_->NowTicks() + period); } // base::trace_event tracing functionality scoped_refptr OrderedSimpleTaskRunner::AsValue() const { scoped_refptr state = new base::trace_event::TracedValue(); AsValueInto(state.get()); return state; } void OrderedSimpleTaskRunner::AsValueInto( base::trace_event::TracedValue* state) const { state->SetInteger("pending_tasks", base::saturated_cast(pending_tasks_.size())); state->BeginArray("tasks"); for (std::set::const_iterator it = pending_tasks_.begin(); it != pending_tasks_.end(); ++it) { state->BeginDictionary(); it->AsValueInto(state); state->EndDictionary(); } state->EndArray(); state->BeginDictionary("now_src"); state->SetDouble("now_in_ms", (now_src_->NowTicks() - base::TimeTicks()) .InMillisecondsF()); state->EndDictionary(); state->SetBoolean("advance_now", advance_now_); state->SetBoolean("inside_run_tasks_until", inside_run_tasks_until_); state->SetString("max_tasks", base::SizeTToString(max_tasks_)); } base::Callback OrderedSimpleTaskRunner::TaskRunCountBelow( size_t max_tasks) { return base::Bind(&OrderedSimpleTaskRunner::TaskRunCountBelowCallback, max_tasks, base::Owned(new size_t(0))); } bool OrderedSimpleTaskRunner::TaskRunCountBelowCallback(size_t max_tasks, size_t* tasks_run) { return (*tasks_run)++ < max_tasks; } base::Callback OrderedSimpleTaskRunner::TaskExistedInitially() { // base::Bind takes a copy of pending_tasks_ return base::Bind(&OrderedSimpleTaskRunner::TaskExistedInitiallyCallback, base::Unretained(this), pending_tasks_); } bool OrderedSimpleTaskRunner::TaskExistedInitiallyCallback( const std::set& existing_tasks) { return existing_tasks.find(*pending_tasks_.begin()) != existing_tasks.end(); } base::Callback OrderedSimpleTaskRunner::NowBefore( base::TimeTicks stop_at) { return base::Bind(&OrderedSimpleTaskRunner::NowBeforeCallback, base::Unretained(this), stop_at); } bool OrderedSimpleTaskRunner::NowBeforeCallback(base::TimeTicks stop_at) { return NextTaskTime() <= stop_at; } base::Callback OrderedSimpleTaskRunner::AdvanceNow() { return base::Bind(&OrderedSimpleTaskRunner::AdvanceNowCallback, base::Unretained(this)); } bool OrderedSimpleTaskRunner::AdvanceNowCallback() { base::TimeTicks next_task_time = NextTaskTime(); if (now_src_->NowTicks() < next_task_time) { now_src_->Advance(next_task_time - now_src_->NowTicks()); } return true; } } // namespace cc