OLD | NEW |
| (Empty) |
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 #include "cc/raster/task_graph_runner.h" | |
6 | |
7 #include <deque> | |
8 #include <vector> | |
9 | |
10 #include "base/bind.h" | |
11 #include "base/synchronization/lock.h" | |
12 #include "base/threading/simple_thread.h" | |
13 #include "cc/base/container_util.h" | |
14 #include "testing/gtest/include/gtest/gtest.h" | |
15 | |
16 namespace cc { | |
17 namespace { | |
18 | |
19 const int kNamespaceCount = 3; | |
20 | |
21 class TaskGraphRunnerTestBase { | |
22 public: | |
23 struct TaskInfo { | |
24 TaskInfo(int namespace_index, | |
25 unsigned id, | |
26 unsigned dependent_id, | |
27 unsigned dependent_count, | |
28 unsigned priority) | |
29 : namespace_index(namespace_index), | |
30 id(id), | |
31 dependent_id(dependent_id), | |
32 dependent_count(dependent_count), | |
33 priority(priority) {} | |
34 | |
35 int namespace_index; | |
36 unsigned id; | |
37 unsigned dependent_id; | |
38 unsigned dependent_count; | |
39 unsigned priority; | |
40 }; | |
41 | |
42 TaskGraphRunnerTestBase() : task_graph_runner_(new TaskGraphRunner) {} | |
43 | |
44 void ResetIds(int namespace_index) { | |
45 run_task_ids_[namespace_index].clear(); | |
46 on_task_completed_ids_[namespace_index].clear(); | |
47 } | |
48 | |
49 void RunAllTasks(int namespace_index) { | |
50 task_graph_runner_->WaitForTasksToFinishRunning( | |
51 namespace_token_[namespace_index]); | |
52 | |
53 Task::Vector completed_tasks; | |
54 task_graph_runner_->CollectCompletedTasks(namespace_token_[namespace_index], | |
55 &completed_tasks); | |
56 for (Task::Vector::const_iterator it = completed_tasks.begin(); | |
57 it != completed_tasks.end(); | |
58 ++it) { | |
59 FakeTaskImpl* task = static_cast<FakeTaskImpl*>(it->get()); | |
60 task->CompleteOnOriginThread(); | |
61 } | |
62 } | |
63 | |
64 void RunTaskOnWorkerThread(int namespace_index, unsigned id) { | |
65 base::AutoLock lock(run_task_ids_lock_); | |
66 run_task_ids_[namespace_index].push_back(id); | |
67 } | |
68 | |
69 void OnTaskCompleted(int namespace_index, unsigned id) { | |
70 on_task_completed_ids_[namespace_index].push_back(id); | |
71 } | |
72 | |
73 const std::vector<unsigned>& run_task_ids(int namespace_index) { | |
74 return run_task_ids_[namespace_index]; | |
75 } | |
76 | |
77 const std::vector<unsigned>& on_task_completed_ids(int namespace_index) { | |
78 return on_task_completed_ids_[namespace_index]; | |
79 } | |
80 | |
81 void ScheduleTasks(int namespace_index, const std::vector<TaskInfo>& tasks) { | |
82 Task::Vector new_tasks; | |
83 Task::Vector new_dependents; | |
84 TaskGraph new_graph; | |
85 | |
86 for (std::vector<TaskInfo>::const_iterator it = tasks.begin(); | |
87 it != tasks.end(); | |
88 ++it) { | |
89 scoped_refptr<FakeTaskImpl> new_task( | |
90 new FakeTaskImpl(this, it->namespace_index, it->id)); | |
91 new_graph.nodes.push_back( | |
92 TaskGraph::Node(new_task.get(), it->priority, 0u)); | |
93 for (unsigned i = 0; i < it->dependent_count; ++i) { | |
94 scoped_refptr<FakeDependentTaskImpl> new_dependent_task( | |
95 new FakeDependentTaskImpl( | |
96 this, it->namespace_index, it->dependent_id)); | |
97 new_graph.nodes.push_back( | |
98 TaskGraph::Node(new_dependent_task.get(), it->priority, 1u)); | |
99 new_graph.edges.push_back( | |
100 TaskGraph::Edge(new_task.get(), new_dependent_task.get())); | |
101 | |
102 new_dependents.push_back(new_dependent_task.get()); | |
103 } | |
104 | |
105 new_tasks.push_back(new_task.get()); | |
106 } | |
107 | |
108 task_graph_runner_->ScheduleTasks(namespace_token_[namespace_index], | |
109 &new_graph); | |
110 | |
111 dependents_[namespace_index].swap(new_dependents); | |
112 tasks_[namespace_index].swap(new_tasks); | |
113 } | |
114 | |
115 protected: | |
116 class FakeTaskImpl : public Task { | |
117 public: | |
118 FakeTaskImpl(TaskGraphRunnerTestBase* test, int namespace_index, int id) | |
119 : test_(test), namespace_index_(namespace_index), id_(id) {} | |
120 | |
121 // Overridden from Task: | |
122 void RunOnWorkerThread() override { | |
123 test_->RunTaskOnWorkerThread(namespace_index_, id_); | |
124 } | |
125 | |
126 virtual void CompleteOnOriginThread() { | |
127 test_->OnTaskCompleted(namespace_index_, id_); | |
128 } | |
129 | |
130 protected: | |
131 ~FakeTaskImpl() override {} | |
132 | |
133 private: | |
134 TaskGraphRunnerTestBase* test_; | |
135 int namespace_index_; | |
136 int id_; | |
137 | |
138 DISALLOW_COPY_AND_ASSIGN(FakeTaskImpl); | |
139 }; | |
140 | |
141 class FakeDependentTaskImpl : public FakeTaskImpl { | |
142 public: | |
143 FakeDependentTaskImpl(TaskGraphRunnerTestBase* test, | |
144 int namespace_index, | |
145 int id) | |
146 : FakeTaskImpl(test, namespace_index, id) {} | |
147 | |
148 // Overridden from FakeTaskImpl: | |
149 void CompleteOnOriginThread() override {} | |
150 | |
151 private: | |
152 ~FakeDependentTaskImpl() override {} | |
153 | |
154 DISALLOW_COPY_AND_ASSIGN(FakeDependentTaskImpl); | |
155 }; | |
156 | |
157 scoped_ptr<TaskGraphRunner> task_graph_runner_; | |
158 NamespaceToken namespace_token_[kNamespaceCount]; | |
159 Task::Vector tasks_[kNamespaceCount]; | |
160 Task::Vector dependents_[kNamespaceCount]; | |
161 std::vector<unsigned> run_task_ids_[kNamespaceCount]; | |
162 base::Lock run_task_ids_lock_; | |
163 std::vector<unsigned> on_task_completed_ids_[kNamespaceCount]; | |
164 }; | |
165 | |
166 class TaskGraphRunnerTest : public TaskGraphRunnerTestBase, | |
167 public testing::TestWithParam<int>, | |
168 public base::DelegateSimpleThread::Delegate { | |
169 public: | |
170 // Overridden from testing::Test: | |
171 void SetUp() override { | |
172 const size_t num_threads = GetParam(); | |
173 while (workers_.size() < num_threads) { | |
174 scoped_ptr<base::DelegateSimpleThread> worker = | |
175 make_scoped_ptr(new base::DelegateSimpleThread(this, "TestWorker")); | |
176 worker->Start(); | |
177 workers_.push_back(std::move(worker)); | |
178 } | |
179 | |
180 for (int i = 0; i < kNamespaceCount; ++i) | |
181 namespace_token_[i] = task_graph_runner_->GetNamespaceToken(); | |
182 } | |
183 void TearDown() override { | |
184 task_graph_runner_->Shutdown(); | |
185 while (!workers_.empty()) { | |
186 scoped_ptr<base::DelegateSimpleThread> worker = PopFront(&workers_); | |
187 worker->Join(); | |
188 } | |
189 } | |
190 | |
191 private: | |
192 // Overridden from base::DelegateSimpleThread::Delegate: | |
193 void Run() override { task_graph_runner_->Run(); } | |
194 | |
195 std::deque<scoped_ptr<base::DelegateSimpleThread>> workers_; | |
196 }; | |
197 | |
198 TEST_P(TaskGraphRunnerTest, Basic) { | |
199 for (int i = 0; i < kNamespaceCount; ++i) { | |
200 EXPECT_EQ(0u, run_task_ids(i).size()); | |
201 EXPECT_EQ(0u, on_task_completed_ids(i).size()); | |
202 | |
203 ScheduleTasks(i, std::vector<TaskInfo>(1, TaskInfo(i, 0u, 0u, 0u, 0u))); | |
204 } | |
205 | |
206 for (int i = 0; i < kNamespaceCount; ++i) { | |
207 RunAllTasks(i); | |
208 | |
209 EXPECT_EQ(1u, run_task_ids(i).size()); | |
210 EXPECT_EQ(1u, on_task_completed_ids(i).size()); | |
211 } | |
212 | |
213 for (int i = 0; i < kNamespaceCount; ++i) | |
214 ScheduleTasks(i, std::vector<TaskInfo>(1, TaskInfo(i, 0u, 0u, 1u, 0u))); | |
215 | |
216 for (int i = 0; i < kNamespaceCount; ++i) { | |
217 RunAllTasks(i); | |
218 | |
219 EXPECT_EQ(3u, run_task_ids(i).size()); | |
220 EXPECT_EQ(2u, on_task_completed_ids(i).size()); | |
221 } | |
222 | |
223 for (int i = 0; i < kNamespaceCount; ++i) | |
224 ScheduleTasks(i, std::vector<TaskInfo>(1, TaskInfo(i, 0u, 0u, 2u, 0u))); | |
225 | |
226 for (int i = 0; i < kNamespaceCount; ++i) { | |
227 RunAllTasks(i); | |
228 | |
229 EXPECT_EQ(6u, run_task_ids(i).size()); | |
230 EXPECT_EQ(3u, on_task_completed_ids(i).size()); | |
231 } | |
232 } | |
233 | |
234 TEST_P(TaskGraphRunnerTest, Dependencies) { | |
235 for (int i = 0; i < kNamespaceCount; ++i) { | |
236 ScheduleTasks(i, | |
237 std::vector<TaskInfo>(1, | |
238 TaskInfo(i, | |
239 0u, | |
240 1u, | |
241 1u, // 1 dependent | |
242 0u))); | |
243 } | |
244 | |
245 for (int i = 0; i < kNamespaceCount; ++i) { | |
246 RunAllTasks(i); | |
247 | |
248 // Check if task ran before dependent. | |
249 ASSERT_EQ(2u, run_task_ids(i).size()); | |
250 EXPECT_EQ(0u, run_task_ids(i)[0]); | |
251 EXPECT_EQ(1u, run_task_ids(i)[1]); | |
252 ASSERT_EQ(1u, on_task_completed_ids(i).size()); | |
253 EXPECT_EQ(0u, on_task_completed_ids(i)[0]); | |
254 } | |
255 | |
256 for (int i = 0; i < kNamespaceCount; ++i) { | |
257 ScheduleTasks(i, | |
258 std::vector<TaskInfo>(1, | |
259 TaskInfo(i, | |
260 2u, | |
261 3u, | |
262 2u, // 2 dependents | |
263 0u))); | |
264 } | |
265 | |
266 for (int i = 0; i < kNamespaceCount; ++i) { | |
267 RunAllTasks(i); | |
268 | |
269 // Task should only run once. | |
270 ASSERT_EQ(5u, run_task_ids(i).size()); | |
271 EXPECT_EQ(2u, run_task_ids(i)[2]); | |
272 EXPECT_EQ(3u, run_task_ids(i)[3]); | |
273 EXPECT_EQ(3u, run_task_ids(i)[4]); | |
274 ASSERT_EQ(2u, on_task_completed_ids(i).size()); | |
275 EXPECT_EQ(2u, on_task_completed_ids(i)[1]); | |
276 } | |
277 } | |
278 | |
279 INSTANTIATE_TEST_CASE_P(TaskGraphRunnerTests, | |
280 TaskGraphRunnerTest, | |
281 ::testing::Range(1, 5)); | |
282 | |
283 class TaskGraphRunnerSingleThreadTest | |
284 : public TaskGraphRunnerTestBase, | |
285 public testing::Test, | |
286 public base::DelegateSimpleThread::Delegate { | |
287 public: | |
288 // Overridden from testing::Test: | |
289 void SetUp() override { | |
290 worker_.reset(new base::DelegateSimpleThread(this, "TestWorker")); | |
291 worker_->Start(); | |
292 | |
293 for (int i = 0; i < kNamespaceCount; ++i) | |
294 namespace_token_[i] = task_graph_runner_->GetNamespaceToken(); | |
295 } | |
296 void TearDown() override { | |
297 task_graph_runner_->Shutdown(); | |
298 worker_->Join(); | |
299 } | |
300 | |
301 private: | |
302 // Overridden from base::DelegateSimpleThread::Delegate: | |
303 void Run() override { task_graph_runner_->Run(); } | |
304 | |
305 scoped_ptr<base::DelegateSimpleThread> worker_; | |
306 }; | |
307 | |
308 TEST_F(TaskGraphRunnerSingleThreadTest, Priority) { | |
309 for (int i = 0; i < kNamespaceCount; ++i) { | |
310 TaskInfo tasks[] = {TaskInfo(i, 0u, 2u, 1u, 1u), // Priority 1 | |
311 TaskInfo(i, 1u, 3u, 1u, 0u) // Priority 0 | |
312 }; | |
313 ScheduleTasks(i, std::vector<TaskInfo>(tasks, tasks + arraysize(tasks))); | |
314 } | |
315 | |
316 for (int i = 0; i < kNamespaceCount; ++i) { | |
317 RunAllTasks(i); | |
318 | |
319 // Check if tasks ran in order of priority. | |
320 ASSERT_EQ(4u, run_task_ids(i).size()); | |
321 EXPECT_EQ(1u, run_task_ids(i)[0]); | |
322 EXPECT_EQ(3u, run_task_ids(i)[1]); | |
323 EXPECT_EQ(0u, run_task_ids(i)[2]); | |
324 EXPECT_EQ(2u, run_task_ids(i)[3]); | |
325 ASSERT_EQ(2u, on_task_completed_ids(i).size()); | |
326 EXPECT_EQ(1u, on_task_completed_ids(i)[0]); | |
327 EXPECT_EQ(0u, on_task_completed_ids(i)[1]); | |
328 } | |
329 } | |
330 | |
331 } // namespace | |
332 } // namespace cc | |
OLD | NEW |