Home | History | Annotate | Download | only in resources
      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     virtual 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     virtual ~FakeTaskImpl() {}
    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     virtual void CompleteOnOriginThread() OVERRIDE {}
    149 
    150    private:
    151     virtual ~FakeDependentTaskImpl() {}
    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   virtual 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   virtual 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   virtual 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   virtual 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   virtual void TearDown() OVERRIDE {
    296     task_graph_runner_->Shutdown();
    297     worker_->Join();
    298   }
    299 
    300  private:
    301   // Overridden from base::DelegateSimpleThread::Delegate:
    302   virtual 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
    332