summaryrefslogtreecommitdiffstats
path: root/cc/raster/task_graph_runner.h
blob: b5ffade02bdbbdbbff85ca230192c2da161bbdf9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CC_RASTER_TASK_GRAPH_RUNNER_H_
#define CC_RASTER_TASK_GRAPH_RUNNER_H_

#include <algorithm>
#include <map>
#include <vector>

#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "cc/base/cc_export.h"

namespace cc {

class TaskGraphRunner;

// A task which can be run by a TaskGraphRunner. To run a Task, it should be
// inserted into a TaskGraph, which can then be scheduled on the
// TaskGraphRunner.
class CC_EXPORT Task : public base::RefCountedThreadSafe<Task> {
 public:
  typedef std::vector<scoped_refptr<Task>> Vector;

  // Subclasses should implement this method. RunOnWorkerThread may be called
  // on any thread, and subclasses are responsible for locking and thread
  // safety.
  virtual void RunOnWorkerThread() = 0;

  void WillRun();
  void DidRun();
  bool HasFinishedRunning() const;

 protected:
  friend class base::RefCountedThreadSafe<Task>;

  Task();
  virtual ~Task();

  bool will_run_;
  bool did_run_;
};

// A task dependency graph describes the order in which to execute a set
// of tasks. Dependencies are represented as edges. Each node is assigned
// a priority and a run count that matches the number of dependencies.
// Priority range from 0 (most favorable scheduling) to UINT_MAX
// (least favorable).
struct CC_EXPORT TaskGraph {
  struct Node {
    typedef std::vector<Node> Vector;

    Node(Task* task, size_t priority, size_t dependencies)
        : task(task), priority(priority), dependencies(dependencies) {}

    Task* task;
    size_t priority;
    size_t dependencies;
  };

  struct Edge {
    typedef std::vector<Edge> Vector;

    Edge(const Task* task, Task* dependent)
        : task(task), dependent(dependent) {}

    const Task* task;
    Task* dependent;
  };

  TaskGraph();
  ~TaskGraph();

  void Swap(TaskGraph* other);
  void Reset();

  Node::Vector nodes;
  Edge::Vector edges;
};

// Opaque identifier that defines a namespace of tasks.
class CC_EXPORT NamespaceToken {
 public:
  NamespaceToken() : id_(0) {}
  ~NamespaceToken() {}

  bool IsValid() const { return id_ != 0; }

 private:
  friend class TaskGraphWorkQueue;

  explicit NamespaceToken(int id) : id_(id) {}

  int id_;
};

// A TaskGraphRunner is an object that runs a set of tasks in the
// order defined by a dependency graph. The TaskGraphRunner interface
// provides a way of decoupling task scheduling from the mechanics of
// how each task will be run. TaskGraphRunner provides the following
// guarantees:
//
//   - Scheduled tasks will not run synchronously. That is, the
//     ScheduleTasks() method will not call Task::Run() directly.
//
//   - Scheduled tasks are guaranteed to run in the order defined by
//     dependency graph.
//
//   - Once scheduled, a task is guaranteed to end up in the
//     |completed_tasks| vector populated by CollectCompletedTasks(),
//     even if it later gets canceled by another call to ScheduleTasks().
//
// TaskGraphRunner does not guarantee that a task with high priority
// runs after a task with low priority. The priority is just a hint
// and a valid TaskGraphRunner might ignore it. Also it does not
// guarantee a memory model for shared data between tasks. (In other
// words, you should use your own synchronization/locking primitives if
// you need to share data between tasks.)
//
// Implementations of TaskGraphRunner should be thread-safe in that all
// methods must be safe to call on any thread.
//
// Some theoretical implementations of TaskGraphRunner:
//
//   - A TaskGraphRunner that uses a thread pool to run scheduled tasks.
//
//   - A TaskGraphRunner that has a method Run() that runs each task.
class CC_EXPORT TaskGraphRunner {
 public:
  // Returns a unique token that can be used to pass a task graph to
  // ScheduleTasks(). Valid tokens are always nonzero.
  virtual NamespaceToken GetNamespaceToken() = 0;

  // Schedule running of tasks in |graph|. Tasks previously scheduled but no
  // longer needed will be canceled unless already running. Canceled tasks are
  // moved to |completed_tasks| without being run. The result is that once
  // scheduled, a task is guaranteed to end up in the |completed_tasks| queue
  // even if it later gets canceled by another call to ScheduleTasks().
  virtual void ScheduleTasks(NamespaceToken token, TaskGraph* graph) = 0;

  // Wait for all scheduled tasks to finish running.
  virtual void WaitForTasksToFinishRunning(NamespaceToken token) = 0;

  // Collect all completed tasks in |completed_tasks|.
  virtual void CollectCompletedTasks(NamespaceToken token,
                                     Task::Vector* completed_tasks) = 0;

 protected:
  virtual ~TaskGraphRunner() {}
};

// Helper class for iterating over all dependents of a task.
class DependentIterator {
 public:
  DependentIterator(TaskGraph* graph, const Task* task)
      : graph_(graph),
        task_(task),
        current_index_(static_cast<size_t>(-1)),
        current_node_(NULL) {
    ++(*this);
  }

  TaskGraph::Node& operator->() const {
    DCHECK_LT(current_index_, graph_->edges.size());
    DCHECK_EQ(graph_->edges[current_index_].task, task_);
    DCHECK(current_node_);
    return *current_node_;
  }

  TaskGraph::Node& operator*() const {
    DCHECK_LT(current_index_, graph_->edges.size());
    DCHECK_EQ(graph_->edges[current_index_].task, task_);
    DCHECK(current_node_);
    return *current_node_;
  }

  // Note: Performance can be improved by keeping edges sorted.
  DependentIterator& operator++() {
    // Find next dependency edge for |task_|.
    do {
      ++current_index_;
      if (current_index_ == graph_->edges.size())
        return *this;
    } while (graph_->edges[current_index_].task != task_);

    // Now find the node for the dependent of this edge.
    TaskGraph::Node::Vector::iterator it = std::find_if(
        graph_->nodes.begin(), graph_->nodes.end(),
        [this](const TaskGraph::Node& node) {
          return node.task == graph_->edges[current_index_].dependent;
        });
    DCHECK(it != graph_->nodes.end());
    current_node_ = &(*it);

    return *this;
  }

  operator bool() const { return current_index_ < graph_->edges.size(); }

 private:
  TaskGraph* graph_;
  const Task* task_;
  size_t current_index_;
  TaskGraph::Node* current_node_;
};

}  // namespace cc

#endif  // CC_RASTER_TASK_GRAPH_RUNNER_H_