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
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
cc / raster / task_graph_work_queue.h [blame]
// Copyright 2014 The Chromium Authors
// 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_WORK_QUEUE_H_
#define CC_RASTER_TASK_GRAPH_WORK_QUEUE_H_
#include <stdint.h>
#include <map>
#include <utility>
#include <vector>
#include "base/containers/contains.h"
#include "base/memory/raw_ptr.h"
#include "base/ranges/algorithm.h"
#include "cc/cc_export.h"
#include "cc/raster/task_graph_runner.h"
namespace cc {
// Implements a queue of incoming TaskGraph work. Designed for use by
// implementations of TaskGraphRunner. Not thread safe, so the caller is
// responsible for all necessary locking.
//
// Tasks in the queue are divided into categories. Tasks from a single graph may
// be put into different categories, each of which is prioritized independently
// from the others. It is up to the implementation of TaskGraphRunner to
// define the meaning of the categories and handle them appropriately.
class CC_EXPORT TaskGraphWorkQueue {
public:
struct TaskNamespace;
struct CC_EXPORT PrioritizedTask {
typedef std::vector<PrioritizedTask> Vector;
PrioritizedTask(scoped_refptr<Task> task,
TaskNamespace* task_namespace,
uint16_t category,
uint16_t priority);
PrioritizedTask(const PrioritizedTask&) = delete;
PrioritizedTask(PrioritizedTask&& other);
~PrioritizedTask();
PrioritizedTask& operator=(const PrioritizedTask&) = delete;
PrioritizedTask& operator=(PrioritizedTask&& other) = default;
scoped_refptr<Task> task;
raw_ptr<TaskNamespace> task_namespace;
uint16_t category;
uint16_t priority;
};
using CategorizedTask = std::pair<uint16_t, scoped_refptr<Task>>;
// Helper classes and static methods used by dependent classes.
struct TaskNamespace {
using Vector = std::vector<raw_ptr<TaskNamespace, VectorExperimental>>;
using ReadyTasks = std::map<uint16_t, PrioritizedTask::Vector>;
TaskNamespace();
TaskNamespace(const TaskNamespace&) = delete;
TaskNamespace(TaskNamespace&& other);
~TaskNamespace();
TaskNamespace& operator=(const TaskNamespace&) = delete;
TaskNamespace& operator=(TaskNamespace&&) = default;
// Current task graph.
TaskGraph graph;
// Map from category to a vector of tasks that are ready to run for that
// category.
ReadyTasks ready_to_run_tasks;
// Completed tasks not yet collected by origin thread.
Task::Vector completed_tasks;
// This set contains all currently running tasks.
std::vector<CategorizedTask> running_tasks;
};
using ReadyNamespaces = std::map<uint16_t, TaskNamespace::Vector>;
TaskGraphWorkQueue();
TaskGraphWorkQueue(const TaskGraphWorkQueue&) = delete;
virtual ~TaskGraphWorkQueue();
TaskGraphWorkQueue& operator=(const TaskGraphWorkQueue&) = delete;
// Generates a NamespaceToken which is guaranteed to be unique within this
// TaskGraphWorkQueue.
NamespaceToken GenerateNamespaceToken();
// Updates a TaskNamespace with a new TaskGraph to run. This cancels any
// previous tasks in the graph being replaced.
void ScheduleTasks(NamespaceToken token, TaskGraph* graph);
// Returns the next task to run for the given category.
PrioritizedTask GetNextTaskToRun(uint16_t category);
// Returns `true` if the task became ready to run.
bool ExternalDependencyCompletedForTask(NamespaceToken token,
scoped_refptr<Task> task);
// Marks a task as completed, adding it to its namespace's list of completed
// tasks and updating the list of |ready_to_run_namespaces|.
void CompleteTask(PrioritizedTask completed_task);
// Helper which populates a vector of completed tasks from the provided
// namespace.
void CollectCompletedTasks(NamespaceToken token,
Task::Vector* completed_tasks);
// Helper which returns the raw TaskNamespace* for the given token. Used to
// allow callers to re-use a TaskNamespace*, reducing the number of lookups
// needed.
TaskNamespace* GetNamespaceForToken(NamespaceToken token) {
auto it = namespaces_.find(token);
if (it == namespaces_.end())
return nullptr;
return &it->second;
}
static bool HasReadyToRunTasksInNamespace(
const TaskNamespace* task_namespace) {
return !base::ranges::all_of(
task_namespace->ready_to_run_tasks, &PrioritizedTask::Vector::empty,
&TaskNamespace::ReadyTasks::value_type::second);
}
static bool HasTasksBlockedOnExternalDependencyInNamespace(
const TaskNamespace* task_namespace) {
return base::ranges::any_of(task_namespace->graph.nodes,
&TaskGraph::Node::has_external_dependency);
}
static bool HasFinishedRunningTasksInNamespace(
const TaskNamespace* task_namespace) {
return task_namespace->running_tasks.empty() &&
!HasReadyToRunTasksInNamespace(task_namespace) &&
!HasTasksBlockedOnExternalDependencyInNamespace(task_namespace);
}
bool HasReadyToRunTasks() const {
return !base::ranges::all_of(ready_to_run_namespaces_,
&TaskNamespace::Vector::empty,
&ReadyNamespaces::value_type::second);
}
bool HasReadyToRunTasksForCategory(uint16_t category) const {
auto found = ready_to_run_namespaces_.find(category);
return found != ready_to_run_namespaces_.end() && !found->second.empty();
}
bool HasAnyNamespaces() const { return !namespaces_.empty(); }
bool HasFinishedRunningTasksInAllNamespaces() {
return base::ranges::all_of(
namespaces_, [](const TaskNamespaceMap::value_type& entry) {
return HasFinishedRunningTasksInNamespace(&entry.second);
});
}
const ReadyNamespaces& ready_to_run_namespaces() const {
return ready_to_run_namespaces_;
}
size_t NumRunningTasks() const {
size_t count = 0;
for (const auto& task_namespace_entry : namespaces_) {
count += task_namespace_entry.second.running_tasks.size();
}
return count;
}
size_t NumRunningTasksForCategory(uint16_t category) const {
size_t count = 0;
for (const auto& task_namespace_entry : namespaces_) {
for (const auto& categorized_task :
task_namespace_entry.second.running_tasks) {
if (categorized_task.first == category) {
++count;
}
}
}
return count;
}
size_t NumReadyTasksForCategory(uint16_t category) const {
auto found = ready_to_run_namespaces_.find(category);
if (found == ready_to_run_namespaces_.end())
return 0;
size_t count = 0;
for (TaskGraphWorkQueue::TaskNamespace* task_namespace_entry :
found->second) {
DCHECK(
base::Contains(task_namespace_entry->ready_to_run_tasks, category));
count += task_namespace_entry->ready_to_run_tasks.at(category).size();
}
return count;
}
// Helper function which ensures that graph dependencies were correctly
// configured.
static bool DependencyMismatch(const TaskGraph* graph);
private:
bool DecrementNodeDependencies(TaskGraph::Node& node,
TaskNamespace* task_namespace,
bool rebuild_heap);
// Helper class used to provide NamespaceToken comparison to TaskNamespaceMap.
class CompareToken {
public:
bool operator()(const NamespaceToken& lhs,
const NamespaceToken& rhs) const {
return lhs.id_ < rhs.id_;
}
};
using TaskNamespaceMap =
std::map<NamespaceToken, TaskNamespace, CompareToken>;
TaskNamespaceMap namespaces_;
// Map from category to a vector of ready to run namespaces for that category.
ReadyNamespaces ready_to_run_namespaces_;
// Provides a unique id to each NamespaceToken.
int next_namespace_id_;
};
} // namespace cc
#endif // CC_RASTER_TASK_GRAPH_WORK_QUEUE_H_