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