• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #ifndef CC_RESOURCES_TASK_GRAPH_RUNNER_H_
6 #define CC_RESOURCES_TASK_GRAPH_RUNNER_H_
7 
8 #include <map>
9 #include <vector>
10 
11 #include "base/logging.h"
12 #include "base/memory/ref_counted.h"
13 #include "base/synchronization/condition_variable.h"
14 #include "cc/base/cc_export.h"
15 
16 namespace cc {
17 
18 class CC_EXPORT Task : public base::RefCountedThreadSafe<Task> {
19  public:
20   typedef std::vector<scoped_refptr<Task> > Vector;
21 
22   virtual void RunOnWorkerThread() = 0;
23 
24   void WillRun();
25   void DidRun();
26   bool HasFinishedRunning() const;
27 
28  protected:
29   friend class base::RefCountedThreadSafe<Task>;
30 
31   Task();
32   virtual ~Task();
33 
34   bool will_run_;
35   bool did_run_;
36 };
37 
38 // Dependencies are represented as edges in a task graph. Each graph node is
39 // assigned a priority and a run count that matches the number of dependencies.
40 // Priority range from 0 (most favorable scheduling) to UINT_MAX (least
41 // favorable).
42 struct CC_EXPORT TaskGraph {
43   struct Node {
44     class TaskComparator {
45      public:
TaskComparatorTaskGraph::Node46       explicit TaskComparator(const Task* task) : task_(task) {}
47 
operatorTaskGraph::Node48       bool operator()(const Node& node) const { return node.task == task_; }
49 
50      private:
51       const Task* task_;
52     };
53 
54     typedef std::vector<Node> Vector;
55 
NodeTaskGraph::Node56     Node(Task* task, unsigned priority, size_t dependencies)
57         : task(task), priority(priority), dependencies(dependencies) {}
58 
59     Task* task;
60     unsigned priority;
61     size_t dependencies;
62   };
63 
64   struct Edge {
65     typedef std::vector<Edge> Vector;
66 
EdgeTaskGraph::Edge67     Edge(const Task* task, Task* dependent)
68         : task(task), dependent(dependent) {}
69 
70     const Task* task;
71     Task* dependent;
72   };
73 
74   TaskGraph();
75   ~TaskGraph();
76 
77   void Swap(TaskGraph* other);
78   void Reset();
79 
80   Node::Vector nodes;
81   Edge::Vector edges;
82 };
83 
84 class TaskGraphRunner;
85 
86 // Opaque identifier that defines a namespace of tasks.
87 class CC_EXPORT NamespaceToken {
88  public:
NamespaceToken()89   NamespaceToken() : id_(0) {}
~NamespaceToken()90   ~NamespaceToken() {}
91 
IsValid()92   bool IsValid() const { return id_ != 0; }
93 
94  private:
95   friend class TaskGraphRunner;
96 
NamespaceToken(int id)97   explicit NamespaceToken(int id) : id_(id) {}
98 
99   int id_;
100 };
101 
102 // A TaskGraphRunner is used to process tasks with dependencies. There can
103 // be any number of TaskGraphRunner instances per thread. Tasks can be scheduled
104 // from any thread and they can be run on any thread.
105 class CC_EXPORT TaskGraphRunner {
106  public:
107   TaskGraphRunner();
108   virtual ~TaskGraphRunner();
109 
110   // Returns a unique token that can be used to pass a task graph to
111   // ScheduleTasks(). Valid tokens are always nonzero.
112   NamespaceToken GetNamespaceToken();
113 
114   // Schedule running of tasks in |graph|. Tasks previously scheduled but no
115   // longer needed will be canceled unless already running. Canceled tasks are
116   // moved to |completed_tasks| without being run. The result is that once
117   // scheduled, a task is guaranteed to end up in the |completed_tasks| queue
118   // even if it later gets canceled by another call to ScheduleTasks().
119   void ScheduleTasks(NamespaceToken token, TaskGraph* graph);
120 
121   // Wait for all scheduled tasks to finish running.
122   void WaitForTasksToFinishRunning(NamespaceToken token);
123 
124   // Collect all completed tasks in |completed_tasks|.
125   void CollectCompletedTasks(NamespaceToken token,
126                              Task::Vector* completed_tasks);
127 
128   // Run tasks until Shutdown() is called.
129   void Run();
130 
131   // Process all pending tasks, but don't wait/sleep. Return as soon as all
132   // tasks that can be run are taken care of.
133   void RunUntilIdle();
134 
135   // Signals the Run method to return when it becomes idle. It will continue to
136   // process tasks and future tasks as long as they are scheduled.
137   // Warning: if the TaskGraphRunner remains busy, it may never quit.
138   void Shutdown();
139 
140  private:
141   struct PrioritizedTask {
142     typedef std::vector<PrioritizedTask> Vector;
143 
PrioritizedTaskPrioritizedTask144     PrioritizedTask(Task* task, unsigned priority)
145         : task(task), priority(priority) {}
146 
147     Task* task;
148     unsigned priority;
149   };
150 
151   typedef std::vector<const Task*> TaskVector;
152 
153   struct TaskNamespace {
154     typedef std::vector<TaskNamespace*> Vector;
155 
156     TaskNamespace();
157     ~TaskNamespace();
158 
159     // Current task graph.
160     TaskGraph graph;
161 
162     // Ordered set of tasks that are ready to run.
163     PrioritizedTask::Vector ready_to_run_tasks;
164 
165     // Completed tasks not yet collected by origin thread.
166     Task::Vector completed_tasks;
167 
168     // This set contains all currently running tasks.
169     TaskVector running_tasks;
170   };
171 
172   typedef std::map<int, TaskNamespace> TaskNamespaceMap;
173 
CompareTaskPriority(const PrioritizedTask & a,const PrioritizedTask & b)174   static bool CompareTaskPriority(const PrioritizedTask& a,
175                                   const PrioritizedTask& b) {
176     // In this system, numerically lower priority is run first.
177     return a.priority > b.priority;
178   }
179 
CompareTaskNamespacePriority(const TaskNamespace * a,const TaskNamespace * b)180   static bool CompareTaskNamespacePriority(const TaskNamespace* a,
181                                            const TaskNamespace* b) {
182     DCHECK(!a->ready_to_run_tasks.empty());
183     DCHECK(!b->ready_to_run_tasks.empty());
184 
185     // Compare based on task priority of the ready_to_run_tasks heap .front()
186     // will hold the max element of the heap, except after pop_heap, when max
187     // element is moved to .back().
188     return CompareTaskPriority(a->ready_to_run_tasks.front(),
189                                b->ready_to_run_tasks.front());
190   }
191 
HasFinishedRunningTasksInNamespace(const TaskNamespace * task_namespace)192   static bool HasFinishedRunningTasksInNamespace(
193       const TaskNamespace* task_namespace) {
194     return task_namespace->running_tasks.empty() &&
195            task_namespace->ready_to_run_tasks.empty();
196   }
197 
198   // Run next task. Caller must acquire |lock_| prior to calling this function
199   // and make sure at least one task is ready to run.
200   void RunTaskWithLockAcquired();
201 
202   // This lock protects all members of this class. Do not read or modify
203   // anything without holding this lock. Do not block while holding this lock.
204   mutable base::Lock lock_;
205 
206   // Condition variable that is waited on by Run() until new tasks are ready to
207   // run or shutdown starts.
208   base::ConditionVariable has_ready_to_run_tasks_cv_;
209 
210   // Condition variable that is waited on by origin threads until a namespace
211   // has finished running all associated tasks.
212   base::ConditionVariable has_namespaces_with_finished_running_tasks_cv_;
213 
214   // Provides a unique id to each NamespaceToken.
215   int next_namespace_id_;
216 
217   // This set contains all namespaces with pending, running or completed tasks
218   // not yet collected.
219   TaskNamespaceMap namespaces_;
220 
221   // Ordered set of task namespaces that have ready to run tasks.
222   TaskNamespace::Vector ready_to_run_namespaces_;
223 
224   // Set during shutdown. Tells Run() to return when no more tasks are pending.
225   bool shutdown_;
226 
227   DISALLOW_COPY_AND_ASSIGN(TaskGraphRunner);
228 };
229 
230 }  // namespace cc
231 
232 #endif  // CC_RESOURCES_TASK_GRAPH_RUNNER_H_
233