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

Side by Side Diff: cc/raster/task_graph_runner.cc

Issue 1449133002: TaskGraphRunner refactor (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: feedback Created 5 years 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 unified diff | Download patch
« no previous file with comments | « cc/raster/task_graph_runner.h ('k') | cc/raster/task_graph_runner_perftest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 #include "cc/raster/task_graph_runner.h" 5 #include "cc/raster/task_graph_runner.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <utility>
8 9
10 #include "base/atomic_sequence_num.h"
9 #include "base/containers/hash_tables.h" 11 #include "base/containers/hash_tables.h"
10 #include "base/strings/stringprintf.h"
11 #include "base/threading/thread_restrictions.h" 12 #include "base/threading/thread_restrictions.h"
12 #include "base/trace_event/trace_event.h" 13 #include "base/trace_event/trace_event.h"
13 14
14 namespace cc { 15 namespace cc {
15 namespace {
16 16
17 // Helper class for iterating over all dependents of a task. 17 Task::Task() : will_run_(false), did_run_(false) {}
18 class DependentIterator {
19 public:
20 DependentIterator(TaskGraph* graph, const Task* task)
21 : graph_(graph),
22 task_(task),
23 current_index_(static_cast<size_t>(-1)),
24 current_node_(NULL) {
25 ++(*this);
26 }
27
28 TaskGraph::Node& operator->() const {
29 DCHECK_LT(current_index_, graph_->edges.size());
30 DCHECK_EQ(graph_->edges[current_index_].task, task_);
31 DCHECK(current_node_);
32 return *current_node_;
33 }
34
35 TaskGraph::Node& operator*() const {
36 DCHECK_LT(current_index_, graph_->edges.size());
37 DCHECK_EQ(graph_->edges[current_index_].task, task_);
38 DCHECK(current_node_);
39 return *current_node_;
40 }
41
42 // Note: Performance can be improved by keeping edges sorted.
43 DependentIterator& operator++() {
44 // Find next dependency edge for |task_|.
45 do {
46 ++current_index_;
47 if (current_index_ == graph_->edges.size())
48 return *this;
49 } while (graph_->edges[current_index_].task != task_);
50
51 // Now find the node for the dependent of this edge.
52 TaskGraph::Node::Vector::iterator it =
53 std::find_if(graph_->nodes.begin(),
54 graph_->nodes.end(),
55 TaskGraph::Node::TaskComparator(
56 graph_->edges[current_index_].dependent));
57 DCHECK(it != graph_->nodes.end());
58 current_node_ = &(*it);
59
60 return *this;
61 }
62
63 operator bool() const { return current_index_ < graph_->edges.size(); }
64
65 private:
66 TaskGraph* graph_;
67 const Task* task_;
68 size_t current_index_;
69 TaskGraph::Node* current_node_;
70 };
71
72 bool DependencyMismatch(const TaskGraph* graph) {
73 // Value storage will be 0-initialized.
74 base::hash_map<const Task*, size_t> dependents;
75 for (const TaskGraph::Edge& edge : graph->edges)
76 dependents[edge.dependent]++;
77
78 for (const TaskGraph::Node& node : graph->nodes) {
79 if (dependents[node.task] != node.dependencies)
80 return true;
81 }
82
83 return false;
84 }
85
86 } // namespace
87
88 Task::Task() : will_run_(false), did_run_(false) {
89 }
90 18
91 Task::~Task() { 19 Task::~Task() {
92 DCHECK(!will_run_); 20 DCHECK(!will_run_);
93 } 21 }
94 22
95 void Task::WillRun() { 23 void Task::WillRun() {
96 DCHECK(!will_run_); 24 DCHECK(!will_run_);
97 DCHECK(!did_run_); 25 DCHECK(!did_run_);
98 will_run_ = true; 26 will_run_ = true;
99 } 27 }
100 28
101 void Task::DidRun() { 29 void Task::DidRun() {
102 DCHECK(will_run_); 30 DCHECK(will_run_);
103 will_run_ = false; 31 will_run_ = false;
104 did_run_ = true; 32 did_run_ = true;
105 } 33 }
106 34
107 bool Task::HasFinishedRunning() const { return did_run_; } 35 bool Task::HasFinishedRunning() const {
36 return did_run_;
37 }
108 38
109 TaskGraph::TaskGraph() {} 39 TaskGraph::TaskGraph() {}
110 40
111 TaskGraph::~TaskGraph() {} 41 TaskGraph::~TaskGraph() {}
112 42
113 void TaskGraph::Swap(TaskGraph* other) { 43 void TaskGraph::Swap(TaskGraph* other) {
114 nodes.swap(other->nodes); 44 nodes.swap(other->nodes);
115 edges.swap(other->edges); 45 edges.swap(other->edges);
116 } 46 }
117 47
118 void TaskGraph::Reset() { 48 void TaskGraph::Reset() {
119 nodes.clear(); 49 nodes.clear();
120 edges.clear(); 50 edges.clear();
121 } 51 }
122 52
123 TaskGraphRunner::TaskNamespace::TaskNamespace() {}
124
125 TaskGraphRunner::TaskNamespace::~TaskNamespace() {}
126
127 TaskGraphRunner::TaskGraphRunner()
128 : lock_(),
129 has_ready_to_run_tasks_cv_(&lock_),
130 has_namespaces_with_finished_running_tasks_cv_(&lock_),
131 next_namespace_id_(1),
132 shutdown_(false) {}
133
134 TaskGraphRunner::~TaskGraphRunner() {
135 {
136 base::AutoLock lock(lock_);
137
138 DCHECK_EQ(0u, ready_to_run_namespaces_.size());
139 DCHECK_EQ(0u, namespaces_.size());
140 }
141 }
142
143 NamespaceToken TaskGraphRunner::GetNamespaceToken() {
144 base::AutoLock lock(lock_);
145
146 NamespaceToken token(next_namespace_id_++);
147 DCHECK(namespaces_.find(token.id_) == namespaces_.end());
148 return token;
149 }
150
151 void TaskGraphRunner::ScheduleTasks(NamespaceToken token, TaskGraph* graph) {
152 TRACE_EVENT2("cc",
153 "TaskGraphRunner::ScheduleTasks",
154 "num_nodes",
155 graph->nodes.size(),
156 "num_edges",
157 graph->edges.size());
158
159 DCHECK(token.IsValid());
160 DCHECK(!DependencyMismatch(graph));
161
162 {
163 base::AutoLock lock(lock_);
164
165 DCHECK(!shutdown_);
166
167 TaskNamespace& task_namespace = namespaces_[token.id_];
168
169 // First adjust number of dependencies to reflect completed tasks.
170 for (Task::Vector::iterator it = task_namespace.completed_tasks.begin();
171 it != task_namespace.completed_tasks.end();
172 ++it) {
173 for (DependentIterator node_it(graph, it->get()); node_it; ++node_it) {
174 TaskGraph::Node& node = *node_it;
175 DCHECK_LT(0u, node.dependencies);
176 node.dependencies--;
177 }
178 }
179
180 // Build new "ready to run" queue and remove nodes from old graph.
181 task_namespace.ready_to_run_tasks.clear();
182 for (TaskGraph::Node::Vector::iterator it = graph->nodes.begin();
183 it != graph->nodes.end();
184 ++it) {
185 TaskGraph::Node& node = *it;
186
187 // Remove any old nodes that are associated with this task. The result is
188 // that the old graph is left with all nodes not present in this graph,
189 // which we use below to determine what tasks need to be canceled.
190 TaskGraph::Node::Vector::iterator old_it =
191 std::find_if(task_namespace.graph.nodes.begin(),
192 task_namespace.graph.nodes.end(),
193 TaskGraph::Node::TaskComparator(node.task));
194 if (old_it != task_namespace.graph.nodes.end()) {
195 std::swap(*old_it, task_namespace.graph.nodes.back());
196 task_namespace.graph.nodes.pop_back();
197 }
198
199 // Task is not ready to run if dependencies are not yet satisfied.
200 if (node.dependencies)
201 continue;
202
203 // Skip if already finished running task.
204 if (node.task->HasFinishedRunning())
205 continue;
206
207 // Skip if already running.
208 if (std::find(task_namespace.running_tasks.begin(),
209 task_namespace.running_tasks.end(),
210 node.task) != task_namespace.running_tasks.end())
211 continue;
212
213 task_namespace.ready_to_run_tasks.push_back(
214 PrioritizedTask(node.task, node.priority));
215 }
216
217 // Rearrange the elements in |ready_to_run_tasks| in such a way that they
218 // form a heap.
219 std::make_heap(task_namespace.ready_to_run_tasks.begin(),
220 task_namespace.ready_to_run_tasks.end(),
221 CompareTaskPriority);
222
223 // Swap task graph.
224 task_namespace.graph.Swap(graph);
225
226 // Determine what tasks in old graph need to be canceled.
227 for (TaskGraph::Node::Vector::iterator it = graph->nodes.begin();
228 it != graph->nodes.end();
229 ++it) {
230 TaskGraph::Node& node = *it;
231
232 // Skip if already finished running task.
233 if (node.task->HasFinishedRunning())
234 continue;
235
236 // Skip if already running.
237 if (std::find(task_namespace.running_tasks.begin(),
238 task_namespace.running_tasks.end(),
239 node.task) != task_namespace.running_tasks.end())
240 continue;
241
242 DCHECK(std::find(task_namespace.completed_tasks.begin(),
243 task_namespace.completed_tasks.end(),
244 node.task) == task_namespace.completed_tasks.end());
245 task_namespace.completed_tasks.push_back(node.task);
246 }
247
248 // Build new "ready to run" task namespaces queue.
249 ready_to_run_namespaces_.clear();
250 for (TaskNamespaceMap::iterator it = namespaces_.begin();
251 it != namespaces_.end();
252 ++it) {
253 if (!it->second.ready_to_run_tasks.empty())
254 ready_to_run_namespaces_.push_back(&it->second);
255 }
256
257 // Rearrange the task namespaces in |ready_to_run_namespaces_| in such a way
258 // that they form a heap.
259 std::make_heap(ready_to_run_namespaces_.begin(),
260 ready_to_run_namespaces_.end(),
261 CompareTaskNamespacePriority);
262
263 // If there is more work available, wake up worker thread.
264 if (!ready_to_run_namespaces_.empty())
265 has_ready_to_run_tasks_cv_.Signal();
266 }
267 }
268
269 void TaskGraphRunner::WaitForTasksToFinishRunning(NamespaceToken token) {
270 TRACE_EVENT0("cc", "TaskGraphRunner::WaitForTasksToFinishRunning");
271
272 DCHECK(token.IsValid());
273
274 {
275 base::AutoLock lock(lock_);
276 base::ThreadRestrictions::ScopedAllowWait allow_wait;
277
278 TaskNamespaceMap::const_iterator it = namespaces_.find(token.id_);
279 if (it == namespaces_.end())
280 return;
281
282 const TaskNamespace& task_namespace = it->second;
283
284 while (!HasFinishedRunningTasksInNamespace(&task_namespace))
285 has_namespaces_with_finished_running_tasks_cv_.Wait();
286
287 // There may be other namespaces that have finished running tasks, so wake
288 // up another origin thread.
289 has_namespaces_with_finished_running_tasks_cv_.Signal();
290 }
291 }
292
293 void TaskGraphRunner::CollectCompletedTasks(NamespaceToken token,
294 Task::Vector* completed_tasks) {
295 TRACE_EVENT0("cc", "TaskGraphRunner::CollectCompletedTasks");
296
297 DCHECK(token.IsValid());
298
299 {
300 base::AutoLock lock(lock_);
301
302 TaskNamespaceMap::iterator it = namespaces_.find(token.id_);
303 if (it == namespaces_.end())
304 return;
305
306 TaskNamespace& task_namespace = it->second;
307
308 DCHECK_EQ(0u, completed_tasks->size());
309 completed_tasks->swap(task_namespace.completed_tasks);
310 if (!HasFinishedRunningTasksInNamespace(&task_namespace))
311 return;
312
313 // Remove namespace if finished running tasks.
314 DCHECK_EQ(0u, task_namespace.completed_tasks.size());
315 DCHECK_EQ(0u, task_namespace.ready_to_run_tasks.size());
316 DCHECK_EQ(0u, task_namespace.running_tasks.size());
317 namespaces_.erase(it);
318 }
319 }
320
321 void TaskGraphRunner::Shutdown() {
322 base::AutoLock lock(lock_);
323
324 DCHECK_EQ(0u, ready_to_run_namespaces_.size());
325 DCHECK_EQ(0u, namespaces_.size());
326
327 DCHECK(!shutdown_);
328 shutdown_ = true;
329
330 // Wake up a worker so it knows it should exit. This will cause all workers
331 // to exit as each will wake up another worker before exiting.
332 has_ready_to_run_tasks_cv_.Signal();
333 }
334
335 void TaskGraphRunner::FlushForTesting() {
336 base::AutoLock lock(lock_);
337
338 while (std::find_if(namespaces_.begin(), namespaces_.end(),
339 [](const TaskNamespaceMap::value_type& entry) {
340 return !HasFinishedRunningTasksInNamespace(
341 &entry.second);
342 }) != namespaces_.end()) {
343 has_namespaces_with_finished_running_tasks_cv_.Wait();
344 }
345 }
346
347 void TaskGraphRunner::Run() {
348 base::AutoLock lock(lock_);
349
350 while (true) {
351 if (ready_to_run_namespaces_.empty()) {
352 // Exit when shutdown is set and no more tasks are pending.
353 if (shutdown_)
354 break;
355
356 // Wait for more tasks.
357 has_ready_to_run_tasks_cv_.Wait();
358 continue;
359 }
360
361 RunTaskWithLockAcquired();
362 }
363
364 // We noticed we should exit. Wake up the next worker so it knows it should
365 // exit as well (because the Shutdown() code only signals once).
366 has_ready_to_run_tasks_cv_.Signal();
367 }
368
369 void TaskGraphRunner::RunUntilIdle() {
370 base::AutoLock lock(lock_);
371
372 while (!ready_to_run_namespaces_.empty())
373 RunTaskWithLockAcquired();
374 }
375
376 void TaskGraphRunner::RunTaskWithLockAcquired() {
377 TRACE_EVENT0("toplevel", "TaskGraphRunner::RunTask");
378
379 lock_.AssertAcquired();
380 DCHECK(!ready_to_run_namespaces_.empty());
381
382 // Take top priority TaskNamespace from |ready_to_run_namespaces_|.
383 std::pop_heap(ready_to_run_namespaces_.begin(),
384 ready_to_run_namespaces_.end(),
385 CompareTaskNamespacePriority);
386 TaskNamespace* task_namespace = ready_to_run_namespaces_.back();
387 ready_to_run_namespaces_.pop_back();
388 DCHECK(!task_namespace->ready_to_run_tasks.empty());
389
390 // Take top priority task from |ready_to_run_tasks|.
391 std::pop_heap(task_namespace->ready_to_run_tasks.begin(),
392 task_namespace->ready_to_run_tasks.end(),
393 CompareTaskPriority);
394 scoped_refptr<Task> task(task_namespace->ready_to_run_tasks.back().task);
395 task_namespace->ready_to_run_tasks.pop_back();
396
397 // Add task namespace back to |ready_to_run_namespaces_| if not empty after
398 // taking top priority task.
399 if (!task_namespace->ready_to_run_tasks.empty()) {
400 ready_to_run_namespaces_.push_back(task_namespace);
401 std::push_heap(ready_to_run_namespaces_.begin(),
402 ready_to_run_namespaces_.end(),
403 CompareTaskNamespacePriority);
404 }
405
406 // Add task to |running_tasks|.
407 task_namespace->running_tasks.push_back(task.get());
408
409 // There may be more work available, so wake up another worker thread.
410 has_ready_to_run_tasks_cv_.Signal();
411
412 // Call WillRun() before releasing |lock_| and running task.
413 task->WillRun();
414
415 {
416 base::AutoUnlock unlock(lock_);
417
418 task->RunOnWorkerThread();
419 }
420
421 // This will mark task as finished running.
422 task->DidRun();
423
424 // Remove task from |running_tasks|.
425 TaskVector::iterator it = std::find(task_namespace->running_tasks.begin(),
426 task_namespace->running_tasks.end(),
427 task.get());
428 DCHECK(it != task_namespace->running_tasks.end());
429 std::swap(*it, task_namespace->running_tasks.back());
430 task_namespace->running_tasks.pop_back();
431
432 // Now iterate over all dependents to decrement dependencies and check if they
433 // are ready to run.
434 bool ready_to_run_namespaces_has_heap_properties = true;
435 for (DependentIterator it(&task_namespace->graph, task.get()); it; ++it) {
436 TaskGraph::Node& dependent_node = *it;
437
438 DCHECK_LT(0u, dependent_node.dependencies);
439 dependent_node.dependencies--;
440 // Task is ready if it has no dependencies. Add it to |ready_to_run_tasks_|.
441 if (!dependent_node.dependencies) {
442 bool was_empty = task_namespace->ready_to_run_tasks.empty();
443 task_namespace->ready_to_run_tasks.push_back(
444 PrioritizedTask(dependent_node.task, dependent_node.priority));
445 std::push_heap(task_namespace->ready_to_run_tasks.begin(),
446 task_namespace->ready_to_run_tasks.end(),
447 CompareTaskPriority);
448 // Task namespace is ready if it has at least one ready to run task. Add
449 // it to |ready_to_run_namespaces_| if it just become ready.
450 if (was_empty) {
451 DCHECK(std::find(ready_to_run_namespaces_.begin(),
452 ready_to_run_namespaces_.end(),
453 task_namespace) == ready_to_run_namespaces_.end());
454 ready_to_run_namespaces_.push_back(task_namespace);
455 }
456 ready_to_run_namespaces_has_heap_properties = false;
457 }
458 }
459
460 // Rearrange the task namespaces in |ready_to_run_namespaces_| in such a way
461 // that they yet again form a heap.
462 if (!ready_to_run_namespaces_has_heap_properties) {
463 std::make_heap(ready_to_run_namespaces_.begin(),
464 ready_to_run_namespaces_.end(),
465 CompareTaskNamespacePriority);
466 }
467
468 // Finally add task to |completed_tasks_|.
469 task_namespace->completed_tasks.push_back(task);
470
471 // If namespace has finished running all tasks, wake up origin thread.
472 if (HasFinishedRunningTasksInNamespace(task_namespace))
473 has_namespaces_with_finished_running_tasks_cv_.Signal();
474 }
475
476 } // namespace cc 53 } // namespace cc
OLDNEW
« no previous file with comments | « cc/raster/task_graph_runner.h ('k') | cc/raster/task_graph_runner_perftest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698