This source file includes following definitions.
- current_node_
- WillRun
- DidRun
- HasFinishedRunning
- Swap
- Reset
- shutdown_
- GetNamespaceToken
- ScheduleTasks
- WaitForTasksToFinishRunning
- CollectCompletedTasks
- Shutdown
- RunUntilIdle
- RunTaskWithLockAcquired
#include "cc/resources/task_graph_runner.h"
#include <algorithm>
#include "base/debug/trace_event.h"
#include "base/strings/stringprintf.h"
#include "base/threading/thread_restrictions.h"
namespace cc {
namespace internal {
namespace {
class DependentIterator {
public:
DependentIterator(TaskGraph* graph, const Task* task)
: graph_(graph), task_(task), current_index_(-1), current_node_(NULL) {
++(*this);
}
TaskGraph::Node& operator->() const {
DCHECK_LT(current_index_, graph_->edges.size());
DCHECK_EQ(graph_->edges[current_index_].task, task_);
DCHECK(current_node_);
return *current_node_;
}
TaskGraph::Node& operator*() const {
DCHECK_LT(current_index_, graph_->edges.size());
DCHECK_EQ(graph_->edges[current_index_].task, task_);
DCHECK(current_node_);
return *current_node_;
}
DependentIterator& operator++() {
do {
++current_index_;
if (current_index_ == graph_->edges.size())
return *this;
} while (graph_->edges[current_index_].task != task_);
TaskGraph::Node::Vector::iterator it =
std::find_if(graph_->nodes.begin(),
graph_->nodes.end(),
TaskGraph::Node::TaskComparator(
graph_->edges[current_index_].dependent));
DCHECK(it != graph_->nodes.end());
current_node_ = &(*it);
return *this;
}
operator bool() const { return current_index_ < graph_->edges.size(); }
private:
TaskGraph* graph_;
const Task* task_;
size_t current_index_;
TaskGraph::Node* current_node_;
};
class DependencyMismatchComparator {
public:
explicit DependencyMismatchComparator(const TaskGraph* graph)
: graph_(graph) {}
bool operator()(const TaskGraph::Node& node) const {
return static_cast<size_t>(std::count_if(graph_->edges.begin(),
graph_->edges.end(),
DependentComparator(node.task))) !=
node.dependencies;
}
private:
class DependentComparator {
public:
explicit DependentComparator(const Task* dependent)
: dependent_(dependent) {}
bool operator()(const TaskGraph::Edge& edge) const {
return edge.dependent == dependent_;
}
private:
const Task* dependent_;
};
const TaskGraph* graph_;
};
}
Task::Task() : did_run_(false) {}
Task::~Task() {}
void Task::WillRun() {
DCHECK(!did_run_);
}
void Task::DidRun() { did_run_ = true; }
bool Task::HasFinishedRunning() const { return did_run_; }
TaskGraph::TaskGraph() {}
TaskGraph::~TaskGraph() {}
void TaskGraph::Swap(TaskGraph* other) {
nodes.swap(other->nodes);
edges.swap(other->edges);
}
void TaskGraph::Reset() {
nodes.clear();
edges.clear();
}
TaskGraphRunner::TaskNamespace::TaskNamespace() {}
TaskGraphRunner::TaskNamespace::~TaskNamespace() {}
TaskGraphRunner::TaskGraphRunner()
: lock_(),
has_ready_to_run_tasks_cv_(&lock_),
has_namespaces_with_finished_running_tasks_cv_(&lock_),
next_namespace_id_(1),
shutdown_(false) {}
TaskGraphRunner::~TaskGraphRunner() {
{
base::AutoLock lock(lock_);
DCHECK_EQ(0u, ready_to_run_namespaces_.size());
DCHECK_EQ(0u, namespaces_.size());
}
}
NamespaceToken TaskGraphRunner::GetNamespaceToken() {
base::AutoLock lock(lock_);
NamespaceToken token(next_namespace_id_++);
DCHECK(namespaces_.find(token.id_) == namespaces_.end());
return token;
}
void TaskGraphRunner::ScheduleTasks(NamespaceToken token, TaskGraph* graph) {
TRACE_EVENT2("cc",
"TaskGraphRunner::ScheduleTasks",
"num_nodes",
graph->nodes.size(),
"num_edges",
graph->edges.size());
DCHECK(token.IsValid());
DCHECK(std::find_if(graph->nodes.begin(),
graph->nodes.end(),
DependencyMismatchComparator(graph)) ==
graph->nodes.end());
{
base::AutoLock lock(lock_);
DCHECK(!shutdown_);
TaskNamespace& task_namespace = namespaces_[token.id_];
for (Task::Vector::iterator it = task_namespace.completed_tasks.begin();
it != task_namespace.completed_tasks.end();
++it) {
for (DependentIterator node_it(graph, it->get()); node_it; ++node_it) {
TaskGraph::Node& node = *node_it;
DCHECK_LT(0u, node.dependencies);
node.dependencies--;
}
}
task_namespace.ready_to_run_tasks.clear();
for (TaskGraph::Node::Vector::iterator it = graph->nodes.begin();
it != graph->nodes.end();
++it) {
TaskGraph::Node& node = *it;
TaskGraph::Node::Vector::iterator old_it =
std::find_if(task_namespace.graph.nodes.begin(),
task_namespace.graph.nodes.end(),
TaskGraph::Node::TaskComparator(node.task));
if (old_it != task_namespace.graph.nodes.end()) {
std::swap(*old_it, task_namespace.graph.nodes.back());
task_namespace.graph.nodes.pop_back();
}
if (node.dependencies)
continue;
if (node.task->HasFinishedRunning())
continue;
if (std::find(task_namespace.running_tasks.begin(),
task_namespace.running_tasks.end(),
node.task) != task_namespace.running_tasks.end())
continue;
task_namespace.ready_to_run_tasks.push_back(
PrioritizedTask(node.task, node.priority));
}
std::make_heap(task_namespace.ready_to_run_tasks.begin(),
task_namespace.ready_to_run_tasks.end(),
CompareTaskPriority);
task_namespace.graph.Swap(graph);
for (TaskGraph::Node::Vector::iterator it = graph->nodes.begin();
it != graph->nodes.end();
++it) {
TaskGraph::Node& node = *it;
if (node.task->HasFinishedRunning())
continue;
if (std::find(task_namespace.running_tasks.begin(),
task_namespace.running_tasks.end(),
node.task) != task_namespace.running_tasks.end())
continue;
DCHECK(std::find(task_namespace.completed_tasks.begin(),
task_namespace.completed_tasks.end(),
node.task) == task_namespace.completed_tasks.end());
task_namespace.completed_tasks.push_back(node.task);
}
ready_to_run_namespaces_.clear();
for (TaskNamespaceMap::iterator it = namespaces_.begin();
it != namespaces_.end();
++it) {
if (!it->second.ready_to_run_tasks.empty())
ready_to_run_namespaces_.push_back(&it->second);
}
std::make_heap(ready_to_run_namespaces_.begin(),
ready_to_run_namespaces_.end(),
CompareTaskNamespacePriority);
if (!ready_to_run_namespaces_.empty())
has_ready_to_run_tasks_cv_.Signal();
}
}
void TaskGraphRunner::WaitForTasksToFinishRunning(NamespaceToken token) {
TRACE_EVENT0("cc", "TaskGraphRunner::WaitForTasksToFinishRunning");
DCHECK(token.IsValid());
{
base::AutoLock lock(lock_);
TaskNamespaceMap::const_iterator it = namespaces_.find(token.id_);
if (it == namespaces_.end())
return;
const TaskNamespace& task_namespace = it->second;
while (!HasFinishedRunningTasksInNamespace(&task_namespace))
has_namespaces_with_finished_running_tasks_cv_.Wait();
has_namespaces_with_finished_running_tasks_cv_.Signal();
}
}
void TaskGraphRunner::CollectCompletedTasks(NamespaceToken token,
Task::Vector* completed_tasks) {
TRACE_EVENT0("cc", "TaskGraphRunner::CollectCompletedTasks");
DCHECK(token.IsValid());
{
base::AutoLock lock(lock_);
TaskNamespaceMap::iterator it = namespaces_.find(token.id_);
if (it == namespaces_.end())
return;
TaskNamespace& task_namespace = it->second;
DCHECK_EQ(0u, completed_tasks->size());
completed_tasks->swap(task_namespace.completed_tasks);
if (!HasFinishedRunningTasksInNamespace(&task_namespace))
return;
DCHECK_EQ(0u, task_namespace.completed_tasks.size());
DCHECK_EQ(0u, task_namespace.ready_to_run_tasks.size());
DCHECK_EQ(0u, task_namespace.running_tasks.size());
namespaces_.erase(it);
}
}
void TaskGraphRunner::Shutdown() {
base::AutoLock lock(lock_);
DCHECK_EQ(0u, ready_to_run_namespaces_.size());
DCHECK_EQ(0u, namespaces_.size());
DCHECK(!shutdown_);
shutdown_ = true;
has_ready_to_run_tasks_cv_.Signal();
}
void TaskGraphRunner::Run() {
base::AutoLock lock(lock_);
while (true) {
if (ready_to_run_namespaces_.empty()) {
if (shutdown_)
break;
has_ready_to_run_tasks_cv_.Wait();
continue;
}
RunTaskWithLockAcquired();
}
has_ready_to_run_tasks_cv_.Signal();
}
void TaskGraphRunner::RunUntilIdle() {
base::AutoLock lock(lock_);
while (!ready_to_run_namespaces_.empty())
RunTaskWithLockAcquired();
}
void TaskGraphRunner::RunTaskWithLockAcquired() {
TRACE_EVENT0("toplevel", "TaskGraphRunner::RunTask");
lock_.AssertAcquired();
DCHECK(!ready_to_run_namespaces_.empty());
std::pop_heap(ready_to_run_namespaces_.begin(),
ready_to_run_namespaces_.end(),
CompareTaskNamespacePriority);
TaskNamespace* task_namespace = ready_to_run_namespaces_.back();
ready_to_run_namespaces_.pop_back();
DCHECK(!task_namespace->ready_to_run_tasks.empty());
std::pop_heap(task_namespace->ready_to_run_tasks.begin(),
task_namespace->ready_to_run_tasks.end(),
CompareTaskPriority);
scoped_refptr<Task> task(task_namespace->ready_to_run_tasks.back().task);
task_namespace->ready_to_run_tasks.pop_back();
if (!task_namespace->ready_to_run_tasks.empty()) {
ready_to_run_namespaces_.push_back(task_namespace);
std::push_heap(ready_to_run_namespaces_.begin(),
ready_to_run_namespaces_.end(),
CompareTaskNamespacePriority);
}
task_namespace->running_tasks.push_back(task.get());
has_ready_to_run_tasks_cv_.Signal();
task->WillRun();
{
base::AutoUnlock unlock(lock_);
task->RunOnWorkerThread();
}
task->DidRun();
TaskVector::iterator it = std::find(task_namespace->running_tasks.begin(),
task_namespace->running_tasks.end(),
task.get());
DCHECK(it != task_namespace->running_tasks.end());
std::swap(*it, task_namespace->running_tasks.back());
task_namespace->running_tasks.pop_back();
bool ready_to_run_namespaces_has_heap_properties = true;
for (DependentIterator it(&task_namespace->graph, task.get()); it; ++it) {
TaskGraph::Node& dependent_node = *it;
DCHECK_LT(0u, dependent_node.dependencies);
dependent_node.dependencies--;
if (!dependent_node.dependencies) {
bool was_empty = task_namespace->ready_to_run_tasks.empty();
task_namespace->ready_to_run_tasks.push_back(
PrioritizedTask(dependent_node.task, dependent_node.priority));
std::push_heap(task_namespace->ready_to_run_tasks.begin(),
task_namespace->ready_to_run_tasks.end(),
CompareTaskPriority);
if (was_empty) {
DCHECK(std::find(ready_to_run_namespaces_.begin(),
ready_to_run_namespaces_.end(),
task_namespace) == ready_to_run_namespaces_.end());
ready_to_run_namespaces_.push_back(task_namespace);
}
ready_to_run_namespaces_has_heap_properties = false;
}
}
if (!ready_to_run_namespaces_has_heap_properties) {
std::make_heap(ready_to_run_namespaces_.begin(),
ready_to_run_namespaces_.end(),
CompareTaskNamespacePriority);
}
task_namespace->completed_tasks.push_back(task);
if (HasFinishedRunningTasksInNamespace(task_namespace))
has_namespaces_with_finished_running_tasks_cv_.Signal();
}
}
}