// 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/debug/trace_event.h" #include "base/debug/trace_event_argument.h" #include "base/strings/string_number_conversions.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::debug::TracedValue(); AsValueInto(state.get()); return state; } void TestOrderablePendingTask::AsValueInto( base::debug::TracedValue* state) const { state->SetInteger("id", task_id_); state->SetInteger("run_at", GetTimeToRun().ToInternalValue()); state->SetString("posted_from", location.ToString()); } OrderedSimpleTaskRunner::OrderedSimpleTaskRunner() : advance_now_(true), now_src_(TestNowSource::Create(0)), inside_run_tasks_until_(false) { } OrderedSimpleTaskRunner::OrderedSimpleTaskRunner( scoped_refptr now_src, bool advance_now) : advance_now_(advance_now), now_src_(now_src), max_tasks_(kAbsoluteMaxTasks), inside_run_tasks_until_(false) { } OrderedSimpleTaskRunner::~OrderedSimpleTaskRunner() {} // 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_->Now(), 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_->Now(), 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; } bool OrderedSimpleTaskRunner::HasPendingTasks() const { return pending_tasks_.size() > 0; } base::TimeTicks OrderedSimpleTaskRunner::NextTaskTime() { if (pending_tasks_.size() <= 0) { return TestNowSource::kAbsoluteMaxNow; } return pending_tasks_.begin()->GetTimeToRun(); } base::TimeDelta OrderedSimpleTaskRunner::DelayToNextTaskTime() { DCHECK(thread_checker_.CalledOnValidThread()); if (pending_tasks_.size() <= 0) { return TestNowSource::kAbsoluteMaxNow - base::TimeTicks(); } base::TimeDelta delay = NextTaskTime() - now_src_->Now(); 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_->Now())); } 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_->Now() < time) now_src_->SetNow(time); // 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_->Now() < time) { now_src_->SetNow(time); } return result; } bool OrderedSimpleTaskRunner::RunForPeriod(base::TimeDelta period) { return RunUntilTime(now_src_->Now() + period); } // base::debug tracing functionality scoped_refptr OrderedSimpleTaskRunner::AsValue() const { scoped_refptr state = new base::debug::TracedValue(); AsValueInto(state.get()); return state; } void OrderedSimpleTaskRunner::AsValueInto( base::debug::TracedValue* state) const { state->SetInteger("pending_tasks", 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"); now_src_->AsValueInto(state); state->EndDictionary(); } 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_->Now() < next_task_time) { now_src_->SetNow(next_task_time); } return true; } } // namespace cc