Reject XMLHTTPRequests Without HTTP Status Success
[chromium-blink-merge.git] / cc / resources / task_graph_runner.cc
blob21a02f93f4203fe1cc5632b07d8d2301a0781736
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/debug/trace_event.h"
10 #include "base/strings/stringprintf.h"
11 #include "base/threading/thread_restrictions.h"
13 namespace cc {
14 namespace internal {
15 namespace {
17 // Helper class for iterating over all dependents of a task.
18 class DependentIterator {
19 public:
20 DependentIterator(TaskGraph* graph, const Task* task)
21 : graph_(graph), task_(task), current_index_(-1), current_node_(NULL) {
22 ++(*this);
25 TaskGraph::Node& operator->() const {
26 DCHECK_LT(current_index_, graph_->edges.size());
27 DCHECK_EQ(graph_->edges[current_index_].task, task_);
28 DCHECK(current_node_);
29 return *current_node_;
32 TaskGraph::Node& operator*() const {
33 DCHECK_LT(current_index_, graph_->edges.size());
34 DCHECK_EQ(graph_->edges[current_index_].task, task_);
35 DCHECK(current_node_);
36 return *current_node_;
39 // Note: Performance can be improved by keeping edges sorted.
40 DependentIterator& operator++() {
41 // Find next dependency edge for |task_|.
42 do {
43 ++current_index_;
44 if (current_index_ == graph_->edges.size())
45 return *this;
46 } while (graph_->edges[current_index_].task != task_);
48 // Now find the node for the dependent of this edge.
49 TaskGraph::Node::Vector::iterator it =
50 std::find_if(graph_->nodes.begin(),
51 graph_->nodes.end(),
52 TaskGraph::Node::TaskComparator(
53 graph_->edges[current_index_].dependent));
54 DCHECK(it != graph_->nodes.end());
55 current_node_ = &(*it);
57 return *this;
60 operator bool() const { return current_index_ < graph_->edges.size(); }
62 private:
63 TaskGraph* graph_;
64 const Task* task_;
65 size_t current_index_;
66 TaskGraph::Node* current_node_;
69 class DependencyMismatchComparator {
70 public:
71 explicit DependencyMismatchComparator(const TaskGraph* graph)
72 : graph_(graph) {}
74 bool operator()(const TaskGraph::Node& node) const {
75 return static_cast<size_t>(std::count_if(graph_->edges.begin(),
76 graph_->edges.end(),
77 DependentComparator(node.task))) !=
78 node.dependencies;
81 private:
82 class DependentComparator {
83 public:
84 explicit DependentComparator(const Task* dependent)
85 : dependent_(dependent) {}
87 bool operator()(const TaskGraph::Edge& edge) const {
88 return edge.dependent == dependent_;
91 private:
92 const Task* dependent_;
95 const TaskGraph* graph_;
98 } // namespace
100 Task::Task() : did_run_(false) {}
102 Task::~Task() {}
104 void Task::WillRun() {
105 DCHECK(!did_run_);
108 void Task::DidRun() { did_run_ = true; }
110 bool Task::HasFinishedRunning() const { return did_run_; }
112 TaskGraph::TaskGraph() {}
114 TaskGraph::~TaskGraph() {}
116 void TaskGraph::Swap(TaskGraph* other) {
117 nodes.swap(other->nodes);
118 edges.swap(other->edges);
121 void TaskGraph::Reset() {
122 nodes.clear();
123 edges.clear();
126 TaskGraphRunner::TaskNamespace::TaskNamespace() {}
128 TaskGraphRunner::TaskNamespace::~TaskNamespace() {}
130 TaskGraphRunner::TaskGraphRunner()
131 : lock_(),
132 has_ready_to_run_tasks_cv_(&lock_),
133 has_namespaces_with_finished_running_tasks_cv_(&lock_),
134 next_namespace_id_(1),
135 shutdown_(false) {}
137 TaskGraphRunner::~TaskGraphRunner() {
139 base::AutoLock lock(lock_);
141 DCHECK_EQ(0u, ready_to_run_namespaces_.size());
142 DCHECK_EQ(0u, namespaces_.size());
146 NamespaceToken TaskGraphRunner::GetNamespaceToken() {
147 base::AutoLock lock(lock_);
149 NamespaceToken token(next_namespace_id_++);
150 DCHECK(namespaces_.find(token.id_) == namespaces_.end());
151 return token;
154 void TaskGraphRunner::ScheduleTasks(NamespaceToken token, TaskGraph* graph) {
155 TRACE_EVENT2("cc",
156 "TaskGraphRunner::ScheduleTasks",
157 "num_nodes",
158 graph->nodes.size(),
159 "num_edges",
160 graph->edges.size());
162 DCHECK(token.IsValid());
163 DCHECK(std::find_if(graph->nodes.begin(),
164 graph->nodes.end(),
165 DependencyMismatchComparator(graph)) ==
166 graph->nodes.end());
169 base::AutoLock lock(lock_);
171 DCHECK(!shutdown_);
173 TaskNamespace& task_namespace = namespaces_[token.id_];
175 // First adjust number of dependencies to reflect completed tasks.
176 for (Task::Vector::iterator it = task_namespace.completed_tasks.begin();
177 it != task_namespace.completed_tasks.end();
178 ++it) {
179 for (DependentIterator node_it(graph, it->get()); node_it; ++node_it) {
180 TaskGraph::Node& node = *node_it;
181 DCHECK_LT(0u, node.dependencies);
182 node.dependencies--;
186 // Build new "ready to run" queue and remove nodes from old graph.
187 task_namespace.ready_to_run_tasks.clear();
188 for (TaskGraph::Node::Vector::iterator it = graph->nodes.begin();
189 it != graph->nodes.end();
190 ++it) {
191 TaskGraph::Node& node = *it;
193 // Remove any old nodes that are associated with this task. The result is
194 // that the old graph is left with all nodes not present in this graph,
195 // which we use below to determine what tasks need to be canceled.
196 TaskGraph::Node::Vector::iterator old_it =
197 std::find_if(task_namespace.graph.nodes.begin(),
198 task_namespace.graph.nodes.end(),
199 TaskGraph::Node::TaskComparator(node.task));
200 if (old_it != task_namespace.graph.nodes.end()) {
201 std::swap(*old_it, task_namespace.graph.nodes.back());
202 task_namespace.graph.nodes.pop_back();
205 // Task is not ready to run if dependencies are not yet satisfied.
206 if (node.dependencies)
207 continue;
209 // Skip if already finished running task.
210 if (node.task->HasFinishedRunning())
211 continue;
213 // Skip if already running.
214 if (std::find(task_namespace.running_tasks.begin(),
215 task_namespace.running_tasks.end(),
216 node.task) != task_namespace.running_tasks.end())
217 continue;
219 task_namespace.ready_to_run_tasks.push_back(
220 PrioritizedTask(node.task, node.priority));
223 // Rearrange the elements in |ready_to_run_tasks| in such a way that they
224 // form a heap.
225 std::make_heap(task_namespace.ready_to_run_tasks.begin(),
226 task_namespace.ready_to_run_tasks.end(),
227 CompareTaskPriority);
229 // Swap task graph.
230 task_namespace.graph.Swap(graph);
232 // Determine what tasks in old graph need to be canceled.
233 for (TaskGraph::Node::Vector::iterator it = graph->nodes.begin();
234 it != graph->nodes.end();
235 ++it) {
236 TaskGraph::Node& node = *it;
238 // Skip if already finished running task.
239 if (node.task->HasFinishedRunning())
240 continue;
242 // Skip if already running.
243 if (std::find(task_namespace.running_tasks.begin(),
244 task_namespace.running_tasks.end(),
245 node.task) != task_namespace.running_tasks.end())
246 continue;
248 DCHECK(std::find(task_namespace.completed_tasks.begin(),
249 task_namespace.completed_tasks.end(),
250 node.task) == task_namespace.completed_tasks.end());
251 task_namespace.completed_tasks.push_back(node.task);
254 // Build new "ready to run" task namespaces queue.
255 ready_to_run_namespaces_.clear();
256 for (TaskNamespaceMap::iterator it = namespaces_.begin();
257 it != namespaces_.end();
258 ++it) {
259 if (!it->second.ready_to_run_tasks.empty())
260 ready_to_run_namespaces_.push_back(&it->second);
263 // Rearrange the task namespaces in |ready_to_run_namespaces_| in such a way
264 // that they form a heap.
265 std::make_heap(ready_to_run_namespaces_.begin(),
266 ready_to_run_namespaces_.end(),
267 CompareTaskNamespacePriority);
269 // If there is more work available, wake up worker thread.
270 if (!ready_to_run_namespaces_.empty())
271 has_ready_to_run_tasks_cv_.Signal();
275 void TaskGraphRunner::WaitForTasksToFinishRunning(NamespaceToken token) {
276 TRACE_EVENT0("cc", "TaskGraphRunner::WaitForTasksToFinishRunning");
278 DCHECK(token.IsValid());
281 base::AutoLock lock(lock_);
283 TaskNamespaceMap::const_iterator it = namespaces_.find(token.id_);
284 if (it == namespaces_.end())
285 return;
287 const TaskNamespace& task_namespace = it->second;
289 while (!HasFinishedRunningTasksInNamespace(&task_namespace))
290 has_namespaces_with_finished_running_tasks_cv_.Wait();
292 // There may be other namespaces that have finished running tasks, so wake
293 // up another origin thread.
294 has_namespaces_with_finished_running_tasks_cv_.Signal();
298 void TaskGraphRunner::CollectCompletedTasks(NamespaceToken token,
299 Task::Vector* completed_tasks) {
300 TRACE_EVENT0("cc", "TaskGraphRunner::CollectCompletedTasks");
302 DCHECK(token.IsValid());
305 base::AutoLock lock(lock_);
307 TaskNamespaceMap::iterator it = namespaces_.find(token.id_);
308 if (it == namespaces_.end())
309 return;
311 TaskNamespace& task_namespace = it->second;
313 DCHECK_EQ(0u, completed_tasks->size());
314 completed_tasks->swap(task_namespace.completed_tasks);
315 if (!HasFinishedRunningTasksInNamespace(&task_namespace))
316 return;
318 // Remove namespace if finished running tasks.
319 DCHECK_EQ(0u, task_namespace.completed_tasks.size());
320 DCHECK_EQ(0u, task_namespace.ready_to_run_tasks.size());
321 DCHECK_EQ(0u, task_namespace.running_tasks.size());
322 namespaces_.erase(it);
326 void TaskGraphRunner::Shutdown() {
327 base::AutoLock lock(lock_);
329 DCHECK_EQ(0u, ready_to_run_namespaces_.size());
330 DCHECK_EQ(0u, namespaces_.size());
332 DCHECK(!shutdown_);
333 shutdown_ = true;
335 // Wake up a worker so it knows it should exit. This will cause all workers
336 // to exit as each will wake up another worker before exiting.
337 has_ready_to_run_tasks_cv_.Signal();
340 void TaskGraphRunner::Run() {
341 base::AutoLock lock(lock_);
343 while (true) {
344 if (ready_to_run_namespaces_.empty()) {
345 // Exit when shutdown is set and no more tasks are pending.
346 if (shutdown_)
347 break;
349 // Wait for more tasks.
350 has_ready_to_run_tasks_cv_.Wait();
351 continue;
354 RunTaskWithLockAcquired();
357 // We noticed we should exit. Wake up the next worker so it knows it should
358 // exit as well (because the Shutdown() code only signals once).
359 has_ready_to_run_tasks_cv_.Signal();
362 void TaskGraphRunner::RunUntilIdle() {
363 base::AutoLock lock(lock_);
365 while (!ready_to_run_namespaces_.empty())
366 RunTaskWithLockAcquired();
369 void TaskGraphRunner::RunTaskWithLockAcquired() {
370 TRACE_EVENT0("toplevel", "TaskGraphRunner::RunTask");
372 lock_.AssertAcquired();
373 DCHECK(!ready_to_run_namespaces_.empty());
375 // Take top priority TaskNamespace from |ready_to_run_namespaces_|.
376 std::pop_heap(ready_to_run_namespaces_.begin(),
377 ready_to_run_namespaces_.end(),
378 CompareTaskNamespacePriority);
379 TaskNamespace* task_namespace = ready_to_run_namespaces_.back();
380 ready_to_run_namespaces_.pop_back();
381 DCHECK(!task_namespace->ready_to_run_tasks.empty());
383 // Take top priority task from |ready_to_run_tasks|.
384 std::pop_heap(task_namespace->ready_to_run_tasks.begin(),
385 task_namespace->ready_to_run_tasks.end(),
386 CompareTaskPriority);
387 scoped_refptr<Task> task(task_namespace->ready_to_run_tasks.back().task);
388 task_namespace->ready_to_run_tasks.pop_back();
390 // Add task namespace back to |ready_to_run_namespaces_| if not empty after
391 // taking top priority task.
392 if (!task_namespace->ready_to_run_tasks.empty()) {
393 ready_to_run_namespaces_.push_back(task_namespace);
394 std::push_heap(ready_to_run_namespaces_.begin(),
395 ready_to_run_namespaces_.end(),
396 CompareTaskNamespacePriority);
399 // Add task to |running_tasks|.
400 task_namespace->running_tasks.push_back(task.get());
402 // There may be more work available, so wake up another worker thread.
403 has_ready_to_run_tasks_cv_.Signal();
405 // Call WillRun() before releasing |lock_| and running task.
406 task->WillRun();
409 base::AutoUnlock unlock(lock_);
411 task->RunOnWorkerThread();
414 // This will mark task as finished running.
415 task->DidRun();
417 // Remove task from |running_tasks|.
418 TaskVector::iterator it = std::find(task_namespace->running_tasks.begin(),
419 task_namespace->running_tasks.end(),
420 task.get());
421 DCHECK(it != task_namespace->running_tasks.end());
422 std::swap(*it, task_namespace->running_tasks.back());
423 task_namespace->running_tasks.pop_back();
425 // Now iterate over all dependents to decrement dependencies and check if they
426 // are ready to run.
427 bool ready_to_run_namespaces_has_heap_properties = true;
428 for (DependentIterator it(&task_namespace->graph, task.get()); it; ++it) {
429 TaskGraph::Node& dependent_node = *it;
431 DCHECK_LT(0u, dependent_node.dependencies);
432 dependent_node.dependencies--;
433 // Task is ready if it has no dependencies. Add it to |ready_to_run_tasks_|.
434 if (!dependent_node.dependencies) {
435 bool was_empty = task_namespace->ready_to_run_tasks.empty();
436 task_namespace->ready_to_run_tasks.push_back(
437 PrioritizedTask(dependent_node.task, dependent_node.priority));
438 std::push_heap(task_namespace->ready_to_run_tasks.begin(),
439 task_namespace->ready_to_run_tasks.end(),
440 CompareTaskPriority);
441 // Task namespace is ready if it has at least one ready to run task. Add
442 // it to |ready_to_run_namespaces_| if it just become ready.
443 if (was_empty) {
444 DCHECK(std::find(ready_to_run_namespaces_.begin(),
445 ready_to_run_namespaces_.end(),
446 task_namespace) == ready_to_run_namespaces_.end());
447 ready_to_run_namespaces_.push_back(task_namespace);
449 ready_to_run_namespaces_has_heap_properties = false;
453 // Rearrange the task namespaces in |ready_to_run_namespaces_| in such a way
454 // that they yet again form a heap.
455 if (!ready_to_run_namespaces_has_heap_properties) {
456 std::make_heap(ready_to_run_namespaces_.begin(),
457 ready_to_run_namespaces_.end(),
458 CompareTaskNamespacePriority);
461 // Finally add task to |completed_tasks_|.
462 task_namespace->completed_tasks.push_back(task);
464 // If namespace has finished running all tasks, wake up origin thread.
465 if (HasFinishedRunningTasksInNamespace(task_namespace))
466 has_namespaces_with_finished_running_tasks_cv_.Signal();
469 } // namespace internal
470 } // namespace cc