OLD | NEW |
---|---|
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #ifndef CC_RASTER_TASK_GRAPH_RUNNER_H_ | 5 #ifndef CC_RASTER_TASK_GRAPH_RUNNER_H_ |
6 #define CC_RASTER_TASK_GRAPH_RUNNER_H_ | 6 #define CC_RASTER_TASK_GRAPH_RUNNER_H_ |
7 | 7 |
8 #include <map> | 8 #include <map> |
9 #include <vector> | 9 #include <vector> |
10 | 10 |
11 #include "base/logging.h" | 11 #include "base/logging.h" |
12 #include "base/memory/ref_counted.h" | 12 #include "base/memory/ref_counted.h" |
13 #include "base/synchronization/condition_variable.h" | 13 #include "base/memory/scoped_ptr.h" |
14 #include "cc/base/cc_export.h" | 14 #include "cc/base/cc_export.h" |
15 | 15 |
16 namespace cc { | 16 namespace cc { |
17 | 17 |
18 class TaskGraphRunner; | |
19 | |
20 // A task which can be run by a TaskGraphRunner. To run a Task, it should be | |
21 // inserted into a TaskGraph, which can then be scheduled on the | |
22 // TaskGraphRunner. | |
18 class CC_EXPORT Task : public base::RefCountedThreadSafe<Task> { | 23 class CC_EXPORT Task : public base::RefCountedThreadSafe<Task> { |
19 public: | 24 public: |
20 typedef std::vector<scoped_refptr<Task>> Vector; | 25 typedef std::vector<scoped_refptr<Task>> Vector; |
21 | 26 |
27 // Subclasses should implement this method. RunOnWorkerThread may be called | |
28 // on any thread, and subclasses are responsible for locking and thread | |
29 // safety. | |
22 virtual void RunOnWorkerThread() = 0; | 30 virtual void RunOnWorkerThread() = 0; |
23 | 31 |
24 void WillRun(); | 32 void WillRun(); |
25 void DidRun(); | 33 void DidRun(); |
26 bool HasFinishedRunning() const; | 34 bool HasFinishedRunning() const; |
27 | 35 |
28 protected: | 36 protected: |
29 friend class base::RefCountedThreadSafe<Task>; | 37 friend class base::RefCountedThreadSafe<Task>; |
30 | 38 |
31 Task(); | 39 Task(); |
32 virtual ~Task(); | 40 virtual ~Task(); |
33 | 41 |
34 bool will_run_; | 42 bool will_run_; |
35 bool did_run_; | 43 bool did_run_; |
36 }; | 44 }; |
37 | 45 |
38 // Dependencies are represented as edges in a task graph. Each graph node is | 46 // A task dependency graph describes the order in which to execute a set |
39 // assigned a priority and a run count that matches the number of dependencies. | 47 // of tasks. Dependencies are represented as edges. Each node is assigned |
40 // Priority range from 0 (most favorable scheduling) to UINT_MAX (least | 48 // a priority and a run count that matches the number of dependencies. |
41 // favorable). | 49 // Priority range from 0 (most favorable scheduling) to UINT_MAX |
50 // (least favorable). | |
42 struct CC_EXPORT TaskGraph { | 51 struct CC_EXPORT TaskGraph { |
43 struct Node { | 52 struct Node { |
44 class TaskComparator { | |
45 public: | |
46 explicit TaskComparator(const Task* task) : task_(task) {} | |
47 | |
48 bool operator()(const Node& node) const { return node.task == task_; } | |
49 | |
50 private: | |
51 const Task* task_; | |
52 }; | |
53 | |
54 typedef std::vector<Node> Vector; | 53 typedef std::vector<Node> Vector; |
55 | 54 |
56 Node(Task* task, size_t priority, size_t dependencies) | 55 Node(Task* task, size_t priority, size_t dependencies) |
57 : task(task), priority(priority), dependencies(dependencies) {} | 56 : task(task), priority(priority), dependencies(dependencies) {} |
58 | 57 |
59 Task* task; | 58 Task* task; |
60 size_t priority; | 59 size_t priority; |
61 size_t dependencies; | 60 size_t dependencies; |
62 }; | 61 }; |
63 | 62 |
(...skipping 10 matching lines...) Expand all Loading... | |
74 TaskGraph(); | 73 TaskGraph(); |
75 ~TaskGraph(); | 74 ~TaskGraph(); |
76 | 75 |
77 void Swap(TaskGraph* other); | 76 void Swap(TaskGraph* other); |
78 void Reset(); | 77 void Reset(); |
79 | 78 |
80 Node::Vector nodes; | 79 Node::Vector nodes; |
81 Edge::Vector edges; | 80 Edge::Vector edges; |
82 }; | 81 }; |
83 | 82 |
84 class TaskGraphRunner; | |
85 | |
86 // Opaque identifier that defines a namespace of tasks. | 83 // Opaque identifier that defines a namespace of tasks. |
87 class CC_EXPORT NamespaceToken { | 84 class CC_EXPORT NamespaceToken { |
88 public: | 85 public: |
89 NamespaceToken() : id_(0) {} | 86 NamespaceToken() : id_(0) {} |
90 ~NamespaceToken() {} | 87 ~NamespaceToken() {} |
91 | 88 |
92 bool IsValid() const { return id_ != 0; } | 89 bool IsValid() const { return id_ != 0; } |
93 | 90 |
94 private: | 91 private: |
95 friend class TaskGraphRunner; | 92 friend class TaskGraphRunner; |
93 friend class TaskGraphWorkQueue; | |
96 | 94 |
97 explicit NamespaceToken(int id) : id_(id) {} | 95 explicit NamespaceToken(int id) : id_(id) {} |
98 | 96 |
99 int id_; | 97 int id_; |
100 }; | 98 }; |
101 | 99 |
102 // A TaskGraphRunner is used to process tasks with dependencies. There can | 100 // A TaskGraphRunner is an object that runs a set of tasks in the |
103 // be any number of TaskGraphRunner instances per thread. Tasks can be scheduled | 101 // order defined by a dependency graph. The TaskGraphRunner interface |
104 // from any thread and they can be run on any thread. | 102 // provides a way of decoupling task scheduling from the mechanics of |
103 // how each task will be run. TaskGraphRunner provides the following | |
104 // guarantees: | |
105 // | |
106 // - Scheduled tasks will not run synchronously. That is, the | |
107 // ScheduleTasks() method will not call Task::Run() directly. | |
108 // | |
109 // - Scheduled tasks are guaranteed to run in the order defined by | |
110 // dependency graph. | |
111 // | |
112 // - Once scheduled, a task is guaranteed to end up in the | |
113 // |completed_tasks| vector populated by CollectCompletedTasks(), | |
114 // even if it later gets canceled by another call to ScheduleTasks(). | |
115 // | |
116 // TaskGraphRunner does not guarantee that a task with high priority | |
117 // runs after a task with low priority. The priority is just a hint | |
118 // and a valid TaskGraphRunner might ignore it. Also it does not | |
119 // guarantee a memory model for shared data between tasks. (In other | |
120 // words, you should use your own synchronization/locking primitives if | |
121 // you need to share data between tasks.) | |
122 // | |
123 // Implementations of TaskGraphRunner should be thread-safe in that all | |
124 // methods must be safe to call on any thread. | |
125 // | |
126 // Some theoretical implementations of TaskGraphRunner: | |
127 // | |
128 // - A TaskGraphRunner that uses a thread pool to run scheduled tasks. | |
129 // | |
130 // - A TaskGraphRunner that has a method Run() that runs each task. | |
105 class CC_EXPORT TaskGraphRunner { | 131 class CC_EXPORT TaskGraphRunner { |
106 public: | 132 public: |
107 TaskGraphRunner(); | 133 virtual ~TaskGraphRunner() {} |
reveman
2015/11/19 16:05:46
nit: please make this protected if possible. code
ericrk
2015/11/23 18:43:37
Done.
| |
108 virtual ~TaskGraphRunner(); | |
109 | 134 |
110 // Returns a unique token that can be used to pass a task graph to | 135 // Returns a unique token that can be used to pass a task graph to |
111 // ScheduleTasks(). Valid tokens are always nonzero. | 136 // ScheduleTasks(). Valid tokens are always nonzero. |
112 NamespaceToken GetNamespaceToken(); | 137 static NamespaceToken GetNamespaceToken(); |
reveman
2015/11/19 16:05:46
Could this be non-static? No reason for namespace
ericrk
2015/11/23 18:43:37
Moved to TaskGraphWorkQueue as non-static
| |
113 | 138 |
114 // Schedule running of tasks in |graph|. Tasks previously scheduled but no | 139 // Schedule running of tasks in |graph|. Tasks previously scheduled but no |
115 // longer needed will be canceled unless already running. Canceled tasks are | 140 // longer needed will be canceled unless already running. Canceled tasks are |
116 // moved to |completed_tasks| without being run. The result is that once | 141 // moved to |completed_tasks| without being run. The result is that once |
117 // scheduled, a task is guaranteed to end up in the |completed_tasks| queue | 142 // scheduled, a task is guaranteed to end up in the |completed_tasks| queue |
118 // even if it later gets canceled by another call to ScheduleTasks(). | 143 // even if it later gets canceled by another call to ScheduleTasks(). |
119 void ScheduleTasks(NamespaceToken token, TaskGraph* graph); | 144 virtual void ScheduleTasks(NamespaceToken token, TaskGraph* graph) = 0; |
120 | 145 |
121 // Wait for all scheduled tasks to finish running. | 146 // Wait for all scheduled tasks to finish running. |
122 void WaitForTasksToFinishRunning(NamespaceToken token); | 147 virtual void WaitForTasksToFinishRunning(NamespaceToken token) = 0; |
123 | 148 |
124 // Collect all completed tasks in |completed_tasks|. | 149 // Collect all completed tasks in |completed_tasks|. |
125 void CollectCompletedTasks(NamespaceToken token, | 150 virtual void CollectCompletedTasks(NamespaceToken token, |
126 Task::Vector* completed_tasks); | 151 Task::Vector* completed_tasks) = 0; |
127 | 152 |
128 // Run tasks until Shutdown() is called. | 153 protected: |
129 void Run(); | 154 // Helper function which ensures that graph dependencies were correctly |
155 // configured. | |
156 static bool DependencyMismatch(const TaskGraph* graph); | |
reveman
2015/11/19 16:05:46
can this be part of TaskGraphWorkQueue instead?
| |
157 }; | |
130 | 158 |
131 // Process all pending tasks, but don't wait/sleep. Return as soon as all | 159 // Helper class for iterating over all dependents of a task. |
132 // tasks that can be run are taken care of. | 160 class DependentIterator { |
133 void RunUntilIdle(); | 161 public: |
162 DependentIterator(TaskGraph* graph, const Task* task) | |
163 : graph_(graph), | |
164 task_(task), | |
165 current_index_(static_cast<size_t>(-1)), | |
166 current_node_(NULL) { | |
167 ++(*this); | |
168 } | |
134 | 169 |
135 // Signals the Run method to return when it becomes idle. It will continue to | 170 TaskGraph::Node& operator->() const { |
136 // process tasks and future tasks as long as they are scheduled. | 171 DCHECK_LT(current_index_, graph_->edges.size()); |
137 // Warning: if the TaskGraphRunner remains busy, it may never quit. | 172 DCHECK_EQ(graph_->edges[current_index_].task, task_); |
138 void Shutdown(); | 173 DCHECK(current_node_); |
174 return *current_node_; | |
175 } | |
139 | 176 |
140 // Wait for all the tasks to finish running on all the namespaces. | 177 TaskGraph::Node& operator*() const { |
141 void FlushForTesting(); | 178 DCHECK_LT(current_index_, graph_->edges.size()); |
179 DCHECK_EQ(graph_->edges[current_index_].task, task_); | |
180 DCHECK(current_node_); | |
181 return *current_node_; | |
182 } | |
183 | |
184 // Note: Performance can be improved by keeping edges sorted. | |
185 DependentIterator& operator++() { | |
186 // Find next dependency edge for |task_|. | |
187 do { | |
188 ++current_index_; | |
189 if (current_index_ == graph_->edges.size()) | |
190 return *this; | |
191 } while (graph_->edges[current_index_].task != task_); | |
192 | |
193 // Now find the node for the dependent of this edge. | |
194 TaskGraph::Node::Vector::iterator it = std::find_if( | |
195 graph_->nodes.begin(), graph_->nodes.end(), | |
196 [this](const TaskGraph::Node& node) { | |
197 return node.task == graph_->edges[current_index_].dependent; | |
198 }); | |
199 DCHECK(it != graph_->nodes.end()); | |
200 current_node_ = &(*it); | |
201 | |
202 return *this; | |
203 } | |
204 | |
205 operator bool() const { return current_index_ < graph_->edges.size(); } | |
142 | 206 |
143 private: | 207 private: |
144 struct PrioritizedTask { | 208 TaskGraph* graph_; |
145 typedef std::vector<PrioritizedTask> Vector; | 209 const Task* task_; |
146 | 210 size_t current_index_; |
147 PrioritizedTask(Task* task, size_t priority) | 211 TaskGraph::Node* current_node_; |
148 : task(task), priority(priority) {} | |
149 | |
150 Task* task; | |
151 size_t priority; | |
152 }; | |
153 | |
154 typedef std::vector<const Task*> TaskVector; | |
155 | |
156 struct TaskNamespace { | |
157 typedef std::vector<TaskNamespace*> Vector; | |
158 | |
159 TaskNamespace(); | |
160 ~TaskNamespace(); | |
161 | |
162 // Current task graph. | |
163 TaskGraph graph; | |
164 | |
165 // Ordered set of tasks that are ready to run. | |
166 PrioritizedTask::Vector ready_to_run_tasks; | |
167 | |
168 // Completed tasks not yet collected by origin thread. | |
169 Task::Vector completed_tasks; | |
170 | |
171 // This set contains all currently running tasks. | |
172 TaskVector running_tasks; | |
173 }; | |
174 | |
175 typedef std::map<int, TaskNamespace> TaskNamespaceMap; | |
176 | |
177 static bool CompareTaskPriority(const PrioritizedTask& a, | |
178 const PrioritizedTask& b) { | |
179 // In this system, numerically lower priority is run first. | |
180 return a.priority > b.priority; | |
181 } | |
182 | |
183 static bool CompareTaskNamespacePriority(const TaskNamespace* a, | |
184 const TaskNamespace* b) { | |
185 DCHECK(!a->ready_to_run_tasks.empty()); | |
186 DCHECK(!b->ready_to_run_tasks.empty()); | |
187 | |
188 // Compare based on task priority of the ready_to_run_tasks heap .front() | |
189 // will hold the max element of the heap, except after pop_heap, when max | |
190 // element is moved to .back(). | |
191 return CompareTaskPriority(a->ready_to_run_tasks.front(), | |
192 b->ready_to_run_tasks.front()); | |
193 } | |
194 | |
195 static bool HasFinishedRunningTasksInNamespace( | |
196 const TaskNamespace* task_namespace) { | |
197 return task_namespace->running_tasks.empty() && | |
198 task_namespace->ready_to_run_tasks.empty(); | |
199 } | |
200 | |
201 // Run next task. Caller must acquire |lock_| prior to calling this function | |
202 // and make sure at least one task is ready to run. | |
203 void RunTaskWithLockAcquired(); | |
204 | |
205 // This lock protects all members of this class. Do not read or modify | |
206 // anything without holding this lock. Do not block while holding this lock. | |
207 mutable base::Lock lock_; | |
208 | |
209 // Condition variable that is waited on by Run() until new tasks are ready to | |
210 // run or shutdown starts. | |
211 base::ConditionVariable has_ready_to_run_tasks_cv_; | |
212 | |
213 // Condition variable that is waited on by origin threads until a namespace | |
214 // has finished running all associated tasks. | |
215 base::ConditionVariable has_namespaces_with_finished_running_tasks_cv_; | |
216 | |
217 // Provides a unique id to each NamespaceToken. | |
218 int next_namespace_id_; | |
219 | |
220 // This set contains all namespaces with pending, running or completed tasks | |
221 // not yet collected. | |
222 TaskNamespaceMap namespaces_; | |
223 | |
224 // Ordered set of task namespaces that have ready to run tasks. | |
225 TaskNamespace::Vector ready_to_run_namespaces_; | |
226 | |
227 // Set during shutdown. Tells Run() to return when no more tasks are pending. | |
228 bool shutdown_; | |
229 | |
230 DISALLOW_COPY_AND_ASSIGN(TaskGraphRunner); | |
231 }; | 212 }; |
232 | 213 |
233 } // namespace cc | 214 } // namespace cc |
234 | 215 |
235 #endif // CC_RASTER_TASK_GRAPH_RUNNER_H_ | 216 #endif // CC_RASTER_TASK_GRAPH_RUNNER_H_ |
OLD | NEW |