GPU workaround to simulate Out of Memory errors with large textures
[chromium-blink-merge.git] / cc / resources / task_graph_runner.cc
blobe0b3f4ce7a65e32e74106f6cf194d1613ed38f10
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
3 // found in the LICENSE file.
5 #include "cc/resources/task_graph_runner.h"
7 #include <algorithm>
9 #include "base/strings/stringprintf.h"
10 #include "base/threading/thread_restrictions.h"
11 #include "base/trace_event/trace_event.h"
13 namespace cc {
14 namespace {
16 // Helper class for iterating over all dependents of a task.
17 class DependentIterator {
18 public:
19 DependentIterator(TaskGraph* graph, const Task* task)
20 : graph_(graph),
21 task_(task),
22 current_index_(static_cast<size_t>(-1)),
23 current_node_(NULL) {
24 ++(*this);
27 TaskGraph::Node& operator->() const {
28 DCHECK_LT(current_index_, graph_->edges.size());
29 DCHECK_EQ(graph_->edges[current_index_].task, task_);
30 DCHECK(current_node_);
31 return *current_node_;
34 TaskGraph::Node& operator*() const {
35 DCHECK_LT(current_index_, graph_->edges.size());
36 DCHECK_EQ(graph_->edges[current_index_].task, task_);
37 DCHECK(current_node_);
38 return *current_node_;
41 // Note: Performance can be improved by keeping edges sorted.
42 DependentIterator& operator++() {
43 // Find next dependency edge for |task_|.
44 do {
45 ++current_index_;
46 if (current_index_ == graph_->edges.size())
47 return *this;
48 } while (graph_->edges[current_index_].task != task_);
50 // Now find the node for the dependent of this edge.
51 TaskGraph::Node::Vector::iterator it =
52 std::find_if(graph_->nodes.begin(),
53 graph_->nodes.end(),
54 TaskGraph::Node::TaskComparator(
55 graph_->edges[current_index_].dependent));
56 DCHECK(it != graph_->nodes.end());
57 current_node_ = &(*it);
59 return *this;
62 operator bool() const { return current_index_ < graph_->edges.size(); }
64 private:
65 TaskGraph* graph_;
66 const Task* task_;
67 size_t current_index_;
68 TaskGraph::Node* current_node_;
71 class DependencyMismatchComparator {
72 public:
73 explicit DependencyMismatchComparator(const TaskGraph* graph)
74 : graph_(graph) {}
76 bool operator()(const TaskGraph::Node& node) const {
77 return static_cast<size_t>(std::count_if(graph_->edges.begin(),
78 graph_->edges.end(),
79 DependentComparator(node.task))) !=
80 node.dependencies;
83 private:
84 class DependentComparator {
85 public:
86 explicit DependentComparator(const Task* dependent)
87 : dependent_(dependent) {}
89 bool operator()(const TaskGraph::Edge& edge) const {
90 return edge.dependent == dependent_;
93 private:
94 const Task* dependent_;
97 const TaskGraph* graph_;
100 } // namespace
102 Task::Task() : will_run_(false), did_run_(false) {
105 Task::~Task() {
106 DCHECK(!will_run_);
109 void Task::WillRun() {
110 DCHECK(!will_run_);
111 DCHECK(!did_run_);
112 will_run_ = true;
115 void Task::DidRun() {
116 DCHECK(will_run_);
117 will_run_ = false;
118 did_run_ = true;
121 bool Task::HasFinishedRunning() const { return did_run_; }
123 TaskGraph::TaskGraph() {}
125 TaskGraph::~TaskGraph() {}
127 void TaskGraph::Swap(TaskGraph* other) {
128 nodes.swap(other->nodes);
129 edges.swap(other->edges);
132 void TaskGraph::Reset() {
133 nodes.clear();
134 edges.clear();
137 TaskGraphRunner::TaskNamespace::TaskNamespace() {}
139 TaskGraphRunner::TaskNamespace::~TaskNamespace() {}
141 TaskGraphRunner::TaskGraphRunner()
142 : lock_(),
143 has_ready_to_run_tasks_cv_(&lock_),
144 has_namespaces_with_finished_running_tasks_cv_(&lock_),
145 next_namespace_id_(1),
146 shutdown_(false) {}
148 TaskGraphRunner::~TaskGraphRunner() {
150 base::AutoLock lock(lock_);
152 DCHECK_EQ(0u, ready_to_run_namespaces_.size());
153 DCHECK_EQ(0u, namespaces_.size());
157 NamespaceToken TaskGraphRunner::GetNamespaceToken() {
158 base::AutoLock lock(lock_);
160 NamespaceToken token(next_namespace_id_++);
161 DCHECK(namespaces_.find(token.id_) == namespaces_.end());
162 return token;
165 void TaskGraphRunner::ScheduleTasks(NamespaceToken token, TaskGraph* graph) {
166 TRACE_EVENT2("cc",
167 "TaskGraphRunner::ScheduleTasks",
168 "num_nodes",
169 graph->nodes.size(),
170 "num_edges",
171 graph->edges.size());
173 DCHECK(token.IsValid());
174 DCHECK(std::find_if(graph->nodes.begin(),
175 graph->nodes.end(),
176 DependencyMismatchComparator(graph)) ==
177 graph->nodes.end());
180 base::AutoLock lock(lock_);
182 DCHECK(!shutdown_);
184 TaskNamespace& task_namespace = namespaces_[token.id_];
186 // First adjust number of dependencies to reflect completed tasks.
187 for (Task::Vector::iterator it = task_namespace.completed_tasks.begin();
188 it != task_namespace.completed_tasks.end();
189 ++it) {
190 for (DependentIterator node_it(graph, it->get()); node_it; ++node_it) {
191 TaskGraph::Node& node = *node_it;
192 DCHECK_LT(0u, node.dependencies);
193 node.dependencies--;
197 // Build new "ready to run" queue and remove nodes from old graph.
198 task_namespace.ready_to_run_tasks.clear();
199 for (TaskGraph::Node::Vector::iterator it = graph->nodes.begin();
200 it != graph->nodes.end();
201 ++it) {
202 TaskGraph::Node& node = *it;
204 // Remove any old nodes that are associated with this task. The result is
205 // that the old graph is left with all nodes not present in this graph,
206 // which we use below to determine what tasks need to be canceled.
207 TaskGraph::Node::Vector::iterator old_it =
208 std::find_if(task_namespace.graph.nodes.begin(),
209 task_namespace.graph.nodes.end(),
210 TaskGraph::Node::TaskComparator(node.task));
211 if (old_it != task_namespace.graph.nodes.end()) {
212 std::swap(*old_it, task_namespace.graph.nodes.back());
213 task_namespace.graph.nodes.pop_back();
216 // Task is not ready to run if dependencies are not yet satisfied.
217 if (node.dependencies)
218 continue;
220 // Skip if already finished running task.
221 if (node.task->HasFinishedRunning())
222 continue;
224 // Skip if already running.
225 if (std::find(task_namespace.running_tasks.begin(),
226 task_namespace.running_tasks.end(),
227 node.task) != task_namespace.running_tasks.end())
228 continue;
230 task_namespace.ready_to_run_tasks.push_back(
231 PrioritizedTask(node.task, node.priority));
234 // Rearrange the elements in |ready_to_run_tasks| in such a way that they
235 // form a heap.
236 std::make_heap(task_namespace.ready_to_run_tasks.begin(),
237 task_namespace.ready_to_run_tasks.end(),
238 CompareTaskPriority);
240 // Swap task graph.
241 task_namespace.graph.Swap(graph);
243 // Determine what tasks in old graph need to be canceled.
244 for (TaskGraph::Node::Vector::iterator it = graph->nodes.begin();
245 it != graph->nodes.end();
246 ++it) {
247 TaskGraph::Node& node = *it;
249 // Skip if already finished running task.
250 if (node.task->HasFinishedRunning())
251 continue;
253 // Skip if already running.
254 if (std::find(task_namespace.running_tasks.begin(),
255 task_namespace.running_tasks.end(),
256 node.task) != task_namespace.running_tasks.end())
257 continue;
259 DCHECK(std::find(task_namespace.completed_tasks.begin(),
260 task_namespace.completed_tasks.end(),
261 node.task) == task_namespace.completed_tasks.end());
262 task_namespace.completed_tasks.push_back(node.task);
265 // Build new "ready to run" task namespaces queue.
266 ready_to_run_namespaces_.clear();
267 for (TaskNamespaceMap::iterator it = namespaces_.begin();
268 it != namespaces_.end();
269 ++it) {
270 if (!it->second.ready_to_run_tasks.empty())
271 ready_to_run_namespaces_.push_back(&it->second);
274 // Rearrange the task namespaces in |ready_to_run_namespaces_| in such a way
275 // that they form a heap.
276 std::make_heap(ready_to_run_namespaces_.begin(),
277 ready_to_run_namespaces_.end(),
278 CompareTaskNamespacePriority);
280 // If there is more work available, wake up worker thread.
281 if (!ready_to_run_namespaces_.empty())
282 has_ready_to_run_tasks_cv_.Signal();
286 void TaskGraphRunner::WaitForTasksToFinishRunning(NamespaceToken token) {
287 TRACE_EVENT0("cc", "TaskGraphRunner::WaitForTasksToFinishRunning");
289 DCHECK(token.IsValid());
292 base::AutoLock lock(lock_);
293 base::ThreadRestrictions::ScopedAllowWait allow_wait;
295 TaskNamespaceMap::const_iterator it = namespaces_.find(token.id_);
296 if (it == namespaces_.end())
297 return;
299 const TaskNamespace& task_namespace = it->second;
301 while (!HasFinishedRunningTasksInNamespace(&task_namespace))
302 has_namespaces_with_finished_running_tasks_cv_.Wait();
304 // There may be other namespaces that have finished running tasks, so wake
305 // up another origin thread.
306 has_namespaces_with_finished_running_tasks_cv_.Signal();
310 void TaskGraphRunner::CollectCompletedTasks(NamespaceToken token,
311 Task::Vector* completed_tasks) {
312 TRACE_EVENT0("cc", "TaskGraphRunner::CollectCompletedTasks");
314 DCHECK(token.IsValid());
317 base::AutoLock lock(lock_);
319 TaskNamespaceMap::iterator it = namespaces_.find(token.id_);
320 if (it == namespaces_.end())
321 return;
323 TaskNamespace& task_namespace = it->second;
325 DCHECK_EQ(0u, completed_tasks->size());
326 completed_tasks->swap(task_namespace.completed_tasks);
327 if (!HasFinishedRunningTasksInNamespace(&task_namespace))
328 return;
330 // Remove namespace if finished running tasks.
331 DCHECK_EQ(0u, task_namespace.completed_tasks.size());
332 DCHECK_EQ(0u, task_namespace.ready_to_run_tasks.size());
333 DCHECK_EQ(0u, task_namespace.running_tasks.size());
334 namespaces_.erase(it);
338 void TaskGraphRunner::Shutdown() {
339 base::AutoLock lock(lock_);
341 DCHECK_EQ(0u, ready_to_run_namespaces_.size());
342 DCHECK_EQ(0u, namespaces_.size());
344 DCHECK(!shutdown_);
345 shutdown_ = true;
347 // Wake up a worker so it knows it should exit. This will cause all workers
348 // to exit as each will wake up another worker before exiting.
349 has_ready_to_run_tasks_cv_.Signal();
352 void TaskGraphRunner::Run() {
353 base::AutoLock lock(lock_);
355 while (true) {
356 if (ready_to_run_namespaces_.empty()) {
357 // Exit when shutdown is set and no more tasks are pending.
358 if (shutdown_)
359 break;
361 // Wait for more tasks.
362 has_ready_to_run_tasks_cv_.Wait();
363 continue;
366 RunTaskWithLockAcquired();
369 // We noticed we should exit. Wake up the next worker so it knows it should
370 // exit as well (because the Shutdown() code only signals once).
371 has_ready_to_run_tasks_cv_.Signal();
374 void TaskGraphRunner::RunUntilIdle() {
375 base::AutoLock lock(lock_);
377 while (!ready_to_run_namespaces_.empty())
378 RunTaskWithLockAcquired();
381 void TaskGraphRunner::RunTaskWithLockAcquired() {
382 TRACE_EVENT0("toplevel", "TaskGraphRunner::RunTask");
384 lock_.AssertAcquired();
385 DCHECK(!ready_to_run_namespaces_.empty());
387 // Take top priority TaskNamespace from |ready_to_run_namespaces_|.
388 std::pop_heap(ready_to_run_namespaces_.begin(),
389 ready_to_run_namespaces_.end(),
390 CompareTaskNamespacePriority);
391 TaskNamespace* task_namespace = ready_to_run_namespaces_.back();
392 ready_to_run_namespaces_.pop_back();
393 DCHECK(!task_namespace->ready_to_run_tasks.empty());
395 // Take top priority task from |ready_to_run_tasks|.
396 std::pop_heap(task_namespace->ready_to_run_tasks.begin(),
397 task_namespace->ready_to_run_tasks.end(),
398 CompareTaskPriority);
399 scoped_refptr<Task> task(task_namespace->ready_to_run_tasks.back().task);
400 task_namespace->ready_to_run_tasks.pop_back();
402 // Add task namespace back to |ready_to_run_namespaces_| if not empty after
403 // taking top priority task.
404 if (!task_namespace->ready_to_run_tasks.empty()) {
405 ready_to_run_namespaces_.push_back(task_namespace);
406 std::push_heap(ready_to_run_namespaces_.begin(),
407 ready_to_run_namespaces_.end(),
408 CompareTaskNamespacePriority);
411 // Add task to |running_tasks|.
412 task_namespace->running_tasks.push_back(task.get());
414 // There may be more work available, so wake up another worker thread.
415 has_ready_to_run_tasks_cv_.Signal();
417 // Call WillRun() before releasing |lock_| and running task.
418 task->WillRun();
421 base::AutoUnlock unlock(lock_);
423 task->RunOnWorkerThread();
426 // This will mark task as finished running.
427 task->DidRun();
429 // Remove task from |running_tasks|.
430 TaskVector::iterator it = std::find(task_namespace->running_tasks.begin(),
431 task_namespace->running_tasks.end(),
432 task.get());
433 DCHECK(it != task_namespace->running_tasks.end());
434 std::swap(*it, task_namespace->running_tasks.back());
435 task_namespace->running_tasks.pop_back();
437 // Now iterate over all dependents to decrement dependencies and check if they
438 // are ready to run.
439 bool ready_to_run_namespaces_has_heap_properties = true;
440 for (DependentIterator it(&task_namespace->graph, task.get()); it; ++it) {
441 TaskGraph::Node& dependent_node = *it;
443 DCHECK_LT(0u, dependent_node.dependencies);
444 dependent_node.dependencies--;
445 // Task is ready if it has no dependencies. Add it to |ready_to_run_tasks_|.
446 if (!dependent_node.dependencies) {
447 bool was_empty = task_namespace->ready_to_run_tasks.empty();
448 task_namespace->ready_to_run_tasks.push_back(
449 PrioritizedTask(dependent_node.task, dependent_node.priority));
450 std::push_heap(task_namespace->ready_to_run_tasks.begin(),
451 task_namespace->ready_to_run_tasks.end(),
452 CompareTaskPriority);
453 // Task namespace is ready if it has at least one ready to run task. Add
454 // it to |ready_to_run_namespaces_| if it just become ready.
455 if (was_empty) {
456 DCHECK(std::find(ready_to_run_namespaces_.begin(),
457 ready_to_run_namespaces_.end(),
458 task_namespace) == ready_to_run_namespaces_.end());
459 ready_to_run_namespaces_.push_back(task_namespace);
461 ready_to_run_namespaces_has_heap_properties = false;
465 // Rearrange the task namespaces in |ready_to_run_namespaces_| in such a way
466 // that they yet again form a heap.
467 if (!ready_to_run_namespaces_has_heap_properties) {
468 std::make_heap(ready_to_run_namespaces_.begin(),
469 ready_to_run_namespaces_.end(),
470 CompareTaskNamespacePriority);
473 // Finally add task to |completed_tasks_|.
474 task_namespace->completed_tasks.push_back(task);
476 // If namespace has finished running all tasks, wake up origin thread.
477 if (HasFinishedRunningTasksInNamespace(task_namespace))
478 has_namespaces_with_finished_running_tasks_cv_.Signal();
481 } // namespace cc