Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2958)

Unified Diff: cc/raster/task_graph_runner.h

Issue 1449133002: TaskGraphRunner refactor (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: feedback Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « cc/raster/synchronous_task_graph_runner_unittest.cc ('k') | cc/raster/task_graph_runner.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: cc/raster/task_graph_runner.h
diff --git a/cc/raster/task_graph_runner.h b/cc/raster/task_graph_runner.h
index d6d419179982eeffe0fadaa7d0cc0a759afcf996..b5ffade02bdbbdbbff85ca230192c2da161bbdf9 100644
--- a/cc/raster/task_graph_runner.h
+++ b/cc/raster/task_graph_runner.h
@@ -5,20 +5,29 @@
#ifndef CC_RASTER_TASK_GRAPH_RUNNER_H_
#define CC_RASTER_TASK_GRAPH_RUNNER_H_
+#include <algorithm>
#include <map>
#include <vector>
#include "base/logging.h"
#include "base/memory/ref_counted.h"
-#include "base/synchronization/condition_variable.h"
+#include "base/memory/scoped_ptr.h"
#include "cc/base/cc_export.h"
namespace cc {
+class TaskGraphRunner;
+
+// A task which can be run by a TaskGraphRunner. To run a Task, it should be
+// inserted into a TaskGraph, which can then be scheduled on the
+// TaskGraphRunner.
class CC_EXPORT Task : public base::RefCountedThreadSafe<Task> {
public:
typedef std::vector<scoped_refptr<Task>> Vector;
+ // Subclasses should implement this method. RunOnWorkerThread may be called
+ // on any thread, and subclasses are responsible for locking and thread
+ // safety.
virtual void RunOnWorkerThread() = 0;
void WillRun();
@@ -35,22 +44,13 @@ class CC_EXPORT Task : public base::RefCountedThreadSafe<Task> {
bool did_run_;
};
-// Dependencies are represented as edges in a task graph. Each graph node is
-// assigned a priority and a run count that matches the number of dependencies.
-// Priority range from 0 (most favorable scheduling) to UINT_MAX (least
-// favorable).
+// A task dependency graph describes the order in which to execute a set
+// of tasks. Dependencies are represented as edges. Each node is assigned
+// a priority and a run count that matches the number of dependencies.
+// Priority range from 0 (most favorable scheduling) to UINT_MAX
+// (least favorable).
struct CC_EXPORT TaskGraph {
struct Node {
- class TaskComparator {
- public:
- explicit TaskComparator(const Task* task) : task_(task) {}
-
- bool operator()(const Node& node) const { return node.task == task_; }
-
- private:
- const Task* task_;
- };
-
typedef std::vector<Node> Vector;
Node(Task* task, size_t priority, size_t dependencies)
@@ -81,8 +81,6 @@ struct CC_EXPORT TaskGraph {
Edge::Vector edges;
};
-class TaskGraphRunner;
-
// Opaque identifier that defines a namespace of tasks.
class CC_EXPORT NamespaceToken {
public:
@@ -92,142 +90,121 @@ class CC_EXPORT NamespaceToken {
bool IsValid() const { return id_ != 0; }
private:
- friend class TaskGraphRunner;
+ friend class TaskGraphWorkQueue;
explicit NamespaceToken(int id) : id_(id) {}
int id_;
};
-// A TaskGraphRunner is used to process tasks with dependencies. There can
-// be any number of TaskGraphRunner instances per thread. Tasks can be scheduled
-// from any thread and they can be run on any thread.
+// A TaskGraphRunner is an object that runs a set of tasks in the
+// order defined by a dependency graph. The TaskGraphRunner interface
+// provides a way of decoupling task scheduling from the mechanics of
+// how each task will be run. TaskGraphRunner provides the following
+// guarantees:
+//
+// - Scheduled tasks will not run synchronously. That is, the
+// ScheduleTasks() method will not call Task::Run() directly.
+//
+// - Scheduled tasks are guaranteed to run in the order defined by
+// dependency graph.
+//
+// - Once scheduled, a task is guaranteed to end up in the
+// |completed_tasks| vector populated by CollectCompletedTasks(),
+// even if it later gets canceled by another call to ScheduleTasks().
+//
+// TaskGraphRunner does not guarantee that a task with high priority
+// runs after a task with low priority. The priority is just a hint
+// and a valid TaskGraphRunner might ignore it. Also it does not
+// guarantee a memory model for shared data between tasks. (In other
+// words, you should use your own synchronization/locking primitives if
+// you need to share data between tasks.)
+//
+// Implementations of TaskGraphRunner should be thread-safe in that all
+// methods must be safe to call on any thread.
+//
+// Some theoretical implementations of TaskGraphRunner:
+//
+// - A TaskGraphRunner that uses a thread pool to run scheduled tasks.
+//
+// - A TaskGraphRunner that has a method Run() that runs each task.
class CC_EXPORT TaskGraphRunner {
public:
- TaskGraphRunner();
- virtual ~TaskGraphRunner();
-
// Returns a unique token that can be used to pass a task graph to
// ScheduleTasks(). Valid tokens are always nonzero.
- NamespaceToken GetNamespaceToken();
+ virtual NamespaceToken GetNamespaceToken() = 0;
// Schedule running of tasks in |graph|. Tasks previously scheduled but no
// longer needed will be canceled unless already running. Canceled tasks are
// moved to |completed_tasks| without being run. The result is that once
// scheduled, a task is guaranteed to end up in the |completed_tasks| queue
// even if it later gets canceled by another call to ScheduleTasks().
- void ScheduleTasks(NamespaceToken token, TaskGraph* graph);
+ virtual void ScheduleTasks(NamespaceToken token, TaskGraph* graph) = 0;
// Wait for all scheduled tasks to finish running.
- void WaitForTasksToFinishRunning(NamespaceToken token);
+ virtual void WaitForTasksToFinishRunning(NamespaceToken token) = 0;
// Collect all completed tasks in |completed_tasks|.
- void CollectCompletedTasks(NamespaceToken token,
- Task::Vector* completed_tasks);
-
- // Run tasks until Shutdown() is called.
- void Run();
-
- // Process all pending tasks, but don't wait/sleep. Return as soon as all
- // tasks that can be run are taken care of.
- void RunUntilIdle();
-
- // Signals the Run method to return when it becomes idle. It will continue to
- // process tasks and future tasks as long as they are scheduled.
- // Warning: if the TaskGraphRunner remains busy, it may never quit.
- void Shutdown();
-
- // Wait for all the tasks to finish running on all the namespaces.
- void FlushForTesting();
-
- private:
- struct PrioritizedTask {
- typedef std::vector<PrioritizedTask> Vector;
-
- PrioritizedTask(Task* task, size_t priority)
- : task(task), priority(priority) {}
-
- Task* task;
- size_t priority;
- };
-
- typedef std::vector<const Task*> TaskVector;
+ virtual void CollectCompletedTasks(NamespaceToken token,
+ Task::Vector* completed_tasks) = 0;
- struct TaskNamespace {
- typedef std::vector<TaskNamespace*> Vector;
-
- TaskNamespace();
- ~TaskNamespace();
-
- // Current task graph.
- TaskGraph graph;
-
- // Ordered set of tasks that are ready to run.
- PrioritizedTask::Vector ready_to_run_tasks;
-
- // Completed tasks not yet collected by origin thread.
- Task::Vector completed_tasks;
-
- // This set contains all currently running tasks.
- TaskVector running_tasks;
- };
-
- typedef std::map<int, TaskNamespace> TaskNamespaceMap;
+ protected:
+ virtual ~TaskGraphRunner() {}
+};
- static bool CompareTaskPriority(const PrioritizedTask& a,
- const PrioritizedTask& b) {
- // In this system, numerically lower priority is run first.
- return a.priority > b.priority;
+// Helper class for iterating over all dependents of a task.
+class DependentIterator {
+ public:
+ DependentIterator(TaskGraph* graph, const Task* task)
+ : graph_(graph),
+ task_(task),
+ current_index_(static_cast<size_t>(-1)),
+ current_node_(NULL) {
+ ++(*this);
}
- static bool CompareTaskNamespacePriority(const TaskNamespace* a,
- const TaskNamespace* b) {
- DCHECK(!a->ready_to_run_tasks.empty());
- DCHECK(!b->ready_to_run_tasks.empty());
-
- // Compare based on task priority of the ready_to_run_tasks heap .front()
- // will hold the max element of the heap, except after pop_heap, when max
- // element is moved to .back().
- return CompareTaskPriority(a->ready_to_run_tasks.front(),
- b->ready_to_run_tasks.front());
+ 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_;
}
- static bool HasFinishedRunningTasksInNamespace(
- const TaskNamespace* task_namespace) {
- return task_namespace->running_tasks.empty() &&
- task_namespace->ready_to_run_tasks.empty();
+ 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_;
}
- // Run next task. Caller must acquire |lock_| prior to calling this function
- // and make sure at least one task is ready to run.
- void RunTaskWithLockAcquired();
-
- // This lock protects all members of this class. Do not read or modify
- // anything without holding this lock. Do not block while holding this lock.
- mutable base::Lock lock_;
-
- // Condition variable that is waited on by Run() until new tasks are ready to
- // run or shutdown starts.
- base::ConditionVariable has_ready_to_run_tasks_cv_;
-
- // Condition variable that is waited on by origin threads until a namespace
- // has finished running all associated tasks.
- base::ConditionVariable has_namespaces_with_finished_running_tasks_cv_;
-
- // Provides a unique id to each NamespaceToken.
- int next_namespace_id_;
-
- // This set contains all namespaces with pending, running or completed tasks
- // not yet collected.
- TaskNamespaceMap namespaces_;
-
- // Ordered set of task namespaces that have ready to run tasks.
- TaskNamespace::Vector ready_to_run_namespaces_;
+ // Note: Performance can be improved by keeping edges sorted.
+ DependentIterator& operator++() {
+ // Find next dependency edge for |task_|.
+ do {
+ ++current_index_;
+ if (current_index_ == graph_->edges.size())
+ return *this;
+ } while (graph_->edges[current_index_].task != task_);
+
+ // Now find the node for the dependent of this edge.
+ TaskGraph::Node::Vector::iterator it = std::find_if(
+ graph_->nodes.begin(), graph_->nodes.end(),
+ [this](const TaskGraph::Node& node) {
+ return node.task == graph_->edges[current_index_].dependent;
+ });
+ DCHECK(it != graph_->nodes.end());
+ current_node_ = &(*it);
+
+ return *this;
+ }
- // Set during shutdown. Tells Run() to return when no more tasks are pending.
- bool shutdown_;
+ operator bool() const { return current_index_ < graph_->edges.size(); }
- DISALLOW_COPY_AND_ASSIGN(TaskGraphRunner);
+ private:
+ TaskGraph* graph_;
+ const Task* task_;
+ size_t current_index_;
+ TaskGraph::Node* current_node_;
};
} // namespace cc
« no previous file with comments | « cc/raster/synchronous_task_graph_runner_unittest.cc ('k') | cc/raster/task_graph_runner.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698