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 |