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_RESOURCES_TASK_GRAPH_RUNNER_H_ | 5 #ifndef CC_RESOURCES_TASK_GRAPH_RUNNER_H_ |
6 #define CC_RESOURCES_TASK_GRAPH_RUNNER_H_ | 6 #define CC_RESOURCES_TASK_GRAPH_RUNNER_H_ |
7 | 7 |
8 #include <queue> | |
9 #include <string> | 8 #include <string> |
10 #include <vector> | 9 #include <vector> |
11 | 10 |
12 #include "base/containers/scoped_ptr_hash_map.h" | 11 #include "base/containers/scoped_ptr_hash_map.h" |
13 #include "base/memory/ref_counted.h" | 12 #include "base/memory/ref_counted.h" |
14 #include "base/memory/scoped_ptr.h" | |
15 #include "base/synchronization/condition_variable.h" | 13 #include "base/synchronization/condition_variable.h" |
16 #include "base/threading/simple_thread.h" | 14 #include "base/threading/simple_thread.h" |
17 #include "cc/base/cc_export.h" | 15 #include "cc/base/cc_export.h" |
18 #include "cc/base/scoped_ptr_deque.h" | 16 #include "cc/base/scoped_ptr_deque.h" |
19 | 17 |
20 namespace cc { | 18 namespace cc { |
21 namespace internal { | 19 namespace internal { |
22 | 20 |
23 class CC_EXPORT Task : public base::RefCountedThreadSafe<Task> { | 21 class CC_EXPORT Task : public base::RefCountedThreadSafe<Task> { |
24 public: | 22 public: |
25 typedef std::vector<scoped_refptr<Task> > Vector; | 23 typedef std::vector<scoped_refptr<Task> > Vector; |
26 | 24 |
27 virtual void RunOnWorkerThread(unsigned thread_index) = 0; | 25 virtual void RunOnWorkerThread(unsigned thread_index) = 0; |
28 | 26 |
29 void WillRun(); | 27 void WillRun(); |
30 void DidRun(); | 28 void DidRun(); |
31 bool HasFinishedRunning() const; | 29 bool HasFinishedRunning() const; |
32 | 30 |
33 protected: | 31 protected: |
34 friend class base::RefCountedThreadSafe<Task>; | 32 friend class base::RefCountedThreadSafe<Task>; |
35 | 33 |
36 Task(); | 34 Task(); |
37 virtual ~Task(); | 35 virtual ~Task(); |
38 | 36 |
39 bool did_run_; | 37 bool did_run_; |
40 }; | 38 }; |
41 | 39 |
42 } // namespace internal | 40 // Dependencies are represented as edges in a task graph. Each graph node |
43 } // namespace cc | 41 // is assigned a priority and a run count that matches the number of |
| 42 // dependencies. |
| 43 struct CC_EXPORT TaskGraph { |
| 44 struct Node { |
| 45 class TaskComparator { |
| 46 public: |
| 47 explicit TaskComparator(const Task* task) : task_(task) {} |
44 | 48 |
45 #if defined(COMPILER_GCC) | 49 bool operator()(const Node& node) const { return node.task == task_; } |
46 namespace BASE_HASH_NAMESPACE { | |
47 template <> | |
48 struct hash<cc::internal::Task*> { | |
49 size_t operator()(cc::internal::Task* ptr) const { | |
50 return hash<size_t>()(reinterpret_cast<size_t>(ptr)); | |
51 } | |
52 }; | |
53 } // namespace BASE_HASH_NAMESPACE | |
54 #endif // COMPILER | |
55 | 50 |
56 namespace cc { | 51 private: |
57 namespace internal { | 52 const Task* task_; |
| 53 }; |
58 | 54 |
59 // Dependencies are represented by edges in a task graph. A graph node | 55 typedef std::vector<Node> Vector; |
60 // store edges as a vector of dependents. Each graph node is assigned | |
61 // a priority and a run count that matches the number of dependencies. | |
62 class CC_EXPORT GraphNode { | |
63 public: | |
64 typedef std::vector<GraphNode*> Vector; | |
65 typedef base::ScopedPtrHashMap<Task*, GraphNode> Map; | |
66 | 56 |
67 GraphNode(Task* task, unsigned priority); | 57 Node(Task* task, unsigned priority, size_t dependencies) |
68 ~GraphNode(); | 58 : task(task), priority(priority), dependencies(dependencies) {} |
69 | 59 |
70 Task* task() { return task_; } | 60 Task* task; |
| 61 unsigned priority; |
| 62 size_t dependencies; |
| 63 }; |
71 | 64 |
72 void add_dependent(GraphNode* dependent) { | 65 struct Edge { |
73 DCHECK(dependent); | 66 typedef std::vector<Edge> Vector; |
74 dependents_.push_back(dependent); | |
75 } | |
76 const Vector& dependents() const { return dependents_; } | |
77 | 67 |
78 unsigned priority() const { return priority_; } | 68 Edge(const Task* task, Task* dependent) |
| 69 : task(task), dependent(dependent) {} |
79 | 70 |
80 unsigned num_dependencies() const { return num_dependencies_; } | 71 const Task* task; |
81 void add_dependency() { ++num_dependencies_; } | 72 Task* dependent; |
82 void remove_dependency() { | 73 }; |
83 DCHECK(num_dependencies_); | |
84 --num_dependencies_; | |
85 } | |
86 | 74 |
87 private: | 75 TaskGraph(); |
88 Task* task_; | 76 ~TaskGraph(); |
89 Vector dependents_; | |
90 unsigned priority_; | |
91 unsigned num_dependencies_; | |
92 | 77 |
93 DISALLOW_COPY_AND_ASSIGN(GraphNode); | 78 void Swap(TaskGraph* other); |
| 79 void Reset(); |
| 80 |
| 81 Node::Vector nodes; |
| 82 Edge::Vector edges; |
94 }; | 83 }; |
95 | 84 |
96 class TaskGraphRunner; | 85 class TaskGraphRunner; |
97 | 86 |
98 // Opaque identifier that defines a namespace of tasks. | 87 // Opaque identifier that defines a namespace of tasks. |
99 class CC_EXPORT NamespaceToken { | 88 class CC_EXPORT NamespaceToken { |
100 public: | 89 public: |
101 NamespaceToken() : id_(0) {} | 90 NamespaceToken() : id_(0) {} |
102 ~NamespaceToken() {} | 91 ~NamespaceToken() {} |
103 | 92 |
104 bool IsValid() const { return id_ != 0; } | 93 bool IsValid() const { return id_ != 0; } |
105 | 94 |
106 private: | 95 private: |
107 friend class TaskGraphRunner; | 96 friend class TaskGraphRunner; |
108 | 97 |
109 explicit NamespaceToken(int id) : id_(id) {} | 98 explicit NamespaceToken(int id) : id_(id) {} |
110 | 99 |
111 int id_; | 100 int id_; |
112 }; | 101 }; |
113 | 102 |
114 // A worker thread pool that runs tasks provided by task graph. Destructor | 103 // A worker thread pool that runs tasks provided by task graph. Destructor |
115 // might block and should not be used on a thread that needs to be responsive. | 104 // might block and should not be used on a thread that needs to be responsive. |
116 class CC_EXPORT TaskGraphRunner : public base::DelegateSimpleThread::Delegate { | 105 class CC_EXPORT TaskGraphRunner : public base::DelegateSimpleThread::Delegate { |
117 public: | 106 public: |
118 typedef GraphNode::Map TaskGraph; | |
119 | |
120 TaskGraphRunner(size_t num_threads, const std::string& thread_name_prefix); | 107 TaskGraphRunner(size_t num_threads, const std::string& thread_name_prefix); |
121 virtual ~TaskGraphRunner(); | 108 virtual ~TaskGraphRunner(); |
122 | 109 |
123 // Returns a unique token that can be used to pass a task graph to | 110 // Returns a unique token that can be used to pass a task graph to |
124 // SetTaskGraph(). Valid tokens are always nonzero. | 111 // SetTaskGraph(). Valid tokens are always nonzero. |
125 NamespaceToken GetNamespaceToken(); | 112 NamespaceToken GetNamespaceToken(); |
126 | 113 |
127 // Schedule running of tasks in |graph|. Tasks previously scheduled but | 114 // Schedule running of tasks in |graph|. Tasks previously scheduled but |
128 // no longer needed will be canceled unless already running. Canceled | 115 // no longer needed will be canceled unless already running. Canceled |
129 // tasks are moved to |completed_tasks| without being run. The result | 116 // tasks are moved to |completed_tasks| without being run. The result |
130 // is that once scheduled, a task is guaranteed to end up in the | 117 // is that once scheduled, a task is guaranteed to end up in the |
131 // |completed_tasks| queue even if it later get canceled by another | 118 // |completed_tasks| queue even if it later gets canceled by another |
132 // call to SetTaskGraph(). | 119 // call to SetTaskGraph(). |
133 void SetTaskGraph(NamespaceToken token, TaskGraph* graph); | 120 void SetTaskGraph(NamespaceToken token, TaskGraph* graph); |
134 | 121 |
135 // Wait for all scheduled tasks to finish running. | 122 // Wait for all scheduled tasks to finish running. |
136 void WaitForTasksToFinishRunning(NamespaceToken token); | 123 void WaitForTasksToFinishRunning(NamespaceToken token); |
137 | 124 |
138 // Collect all completed tasks in |completed_tasks|. | 125 // Collect all completed tasks in |completed_tasks|. |
139 void CollectCompletedTasks(NamespaceToken token, | 126 void CollectCompletedTasks(NamespaceToken token, |
140 Task::Vector* completed_tasks); | 127 Task::Vector* completed_tasks); |
141 | 128 |
142 // Run one task on current thread. Returns false if no tasks are ready | 129 // Run one task on current thread. Returns false if no tasks are ready |
143 // to run. This should only be used by tests. | 130 // to run. This should only be used by tests. |
144 bool RunTaskForTesting(); | 131 bool RunTaskForTesting(); |
145 | 132 |
146 private: | 133 private: |
| 134 struct PrioritizedTask { |
| 135 typedef std::vector<PrioritizedTask> Vector; |
| 136 |
| 137 PrioritizedTask(Task* task, unsigned priority) |
| 138 : task(task), priority(priority) {} |
| 139 |
| 140 Task* task; |
| 141 unsigned priority; |
| 142 }; |
| 143 |
147 struct TaskNamespace { | 144 struct TaskNamespace { |
148 typedef std::vector<TaskNamespace*> Vector; | 145 typedef std::vector<TaskNamespace*> Vector; |
149 | 146 |
150 TaskNamespace(); | 147 TaskNamespace(); |
151 ~TaskNamespace(); | 148 ~TaskNamespace(); |
152 | 149 |
153 // This set contains all pending tasks. | 150 // Current task graph. |
154 TaskGraph pending_tasks; | 151 TaskGraph graph; |
155 // This set contains all currently running tasks. | 152 |
156 TaskGraph running_tasks; | 153 // Ordered set of tasks that are ready to run. |
| 154 PrioritizedTask::Vector ready_to_run_tasks; |
| 155 |
157 // Completed tasks not yet collected by origin thread. | 156 // Completed tasks not yet collected by origin thread. |
158 Task::Vector completed_tasks; | 157 Task::Vector completed_tasks; |
159 // Ordered set of tasks that are ready to run. | 158 |
160 internal::GraphNode::Vector ready_to_run_tasks; | 159 // Number of currently running tasks. |
| 160 size_t num_running_tasks; |
161 }; | 161 }; |
162 | 162 |
163 typedef base::ScopedPtrHashMap<int, TaskNamespace> TaskNamespaceMap; | 163 typedef base::ScopedPtrHashMap<int, TaskNamespace> TaskNamespaceMap; |
164 | 164 |
165 static bool CompareTaskPriority(const internal::GraphNode* a, | 165 static bool CompareTaskPriority(const PrioritizedTask& a, |
166 const internal::GraphNode* b) { | 166 const PrioritizedTask& b) { |
167 // In this system, numerically lower priority is run first. | 167 // In this system, numerically lower priority is run first. |
168 if (a->priority() != b->priority()) | 168 return a.priority > b.priority; |
169 return a->priority() > b->priority(); | |
170 | |
171 // Run task with most dependents first when priority is the same. | |
172 return a->dependents().size() < b->dependents().size(); | |
173 } | 169 } |
174 | 170 |
175 static bool CompareTaskNamespacePriority(const TaskNamespace* a, | 171 static bool CompareTaskNamespacePriority(const TaskNamespace* a, |
176 const TaskNamespace* b) { | 172 const TaskNamespace* b) { |
177 DCHECK(!a->ready_to_run_tasks.empty()); | 173 DCHECK(!a->ready_to_run_tasks.empty()); |
178 DCHECK(!b->ready_to_run_tasks.empty()); | 174 DCHECK(!b->ready_to_run_tasks.empty()); |
179 | 175 |
180 // Compare based on task priority of the ready_to_run_tasks heap | 176 // Compare based on task priority of the ready_to_run_tasks heap |
181 // .front() will hold the max element of the heap, | 177 // .front() will hold the max element of the heap, |
182 // except after pop_heap, when max element is moved to .back(). | 178 // except after pop_heap, when max element is moved to .back(). |
183 return CompareTaskPriority(a->ready_to_run_tasks.front(), | 179 return CompareTaskPriority(a->ready_to_run_tasks.front(), |
184 b->ready_to_run_tasks.front()); | 180 b->ready_to_run_tasks.front()); |
185 } | 181 } |
186 | 182 |
187 static bool HasFinishedRunningTasksInNamespace( | 183 static bool HasFinishedRunningTasksInNamespace( |
188 TaskNamespace* task_namespace) { | 184 const TaskNamespace* task_namespace) { |
189 return task_namespace->pending_tasks.empty() && | 185 return !task_namespace->num_running_tasks && |
190 task_namespace->running_tasks.empty(); | 186 task_namespace->ready_to_run_tasks.empty(); |
191 } | 187 } |
192 | 188 |
193 // Overridden from base::DelegateSimpleThread: | 189 // Overridden from base::DelegateSimpleThread: |
194 virtual void Run() OVERRIDE; | 190 virtual void Run() OVERRIDE; |
195 | 191 |
196 // Run next task. Caller must acquire |lock_| prior to calling this | 192 // Run next task. Caller must acquire |lock_| prior to calling this |
197 // function and make sure at least one task is ready to run. | 193 // function and make sure at least one task is ready to run. |
198 void RunTaskWithLockAcquired(int thread_index); | 194 void RunTaskWithLockAcquired(int thread_index); |
199 | 195 |
200 // This lock protects all members of this class. Do not read or modify | 196 // This lock protects all members of this class. Do not read or modify |
(...skipping 16 matching lines...) Expand all Loading... |
217 // tasks not yet collected. | 213 // tasks not yet collected. |
218 TaskNamespaceMap namespaces_; | 214 TaskNamespaceMap namespaces_; |
219 | 215 |
220 // Ordered set of task namespaces that have ready to run tasks. | 216 // Ordered set of task namespaces that have ready to run tasks. |
221 TaskNamespace::Vector ready_to_run_namespaces_; | 217 TaskNamespace::Vector ready_to_run_namespaces_; |
222 | 218 |
223 // Provides each running thread loop with a unique index. First thread | 219 // Provides each running thread loop with a unique index. First thread |
224 // loop index is 0. | 220 // loop index is 0. |
225 unsigned next_thread_index_; | 221 unsigned next_thread_index_; |
226 | 222 |
| 223 // This set contains all currently running tasks. |
| 224 typedef std::vector<const Task*> TaskVector; |
| 225 TaskVector running_tasks_; |
| 226 |
227 // Set during shutdown. Tells workers to exit when no more tasks | 227 // Set during shutdown. Tells workers to exit when no more tasks |
228 // are pending. | 228 // are pending. |
229 bool shutdown_; | 229 bool shutdown_; |
230 | 230 |
231 ScopedPtrDeque<base::DelegateSimpleThread> workers_; | 231 ScopedPtrDeque<base::DelegateSimpleThread> workers_; |
232 | 232 |
233 DISALLOW_COPY_AND_ASSIGN(TaskGraphRunner); | 233 DISALLOW_COPY_AND_ASSIGN(TaskGraphRunner); |
234 }; | 234 }; |
235 | 235 |
236 } // namespace internal | 236 } // namespace internal |
237 } // namespace cc | 237 } // namespace cc |
238 | 238 |
239 #endif // CC_RESOURCES_TASK_GRAPH_RUNNER_H_ | 239 #endif // CC_RESOURCES_TASK_GRAPH_RUNNER_H_ |
OLD | NEW |