Home | History | Annotate | Download | only in scheduler
      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 "config.h"
      6 #include "platform/scheduler/Scheduler.h"
      7 
      8 #include "platform/TestingPlatformSupport.h"
      9 #include "platform/TraceLocation.h"
     10 #include "public/platform/Platform.h"
     11 #include "public/platform/WebThread.h"
     12 
     13 #include <gmock/gmock.h>
     14 #include <gtest/gtest.h>
     15 #include <string>
     16 #include <vector>
     17 
     18 using blink::Scheduler;
     19 
     20 namespace {
     21 
     22 class SchedulerForTest : public blink::Scheduler {
     23 public:
     24     static void initializeOnMainThread()
     25     {
     26         s_sharedScheduler = new SchedulerForTest();
     27     }
     28 
     29     using Scheduler::Normal;
     30     using Scheduler::CompositorPriority;
     31     using Scheduler::enterSchedulerPolicy;
     32 };
     33 
     34 class TestMainThread : public blink::WebThread {
     35 public:
     36     // blink::WebThread implementation.
     37     virtual void postTask(Task* task) OVERRIDE
     38     {
     39         m_pendingTasks.append(adoptPtr(task));
     40     }
     41 
     42     virtual void postDelayedTask(Task* task, long long delayMs) OVERRIDE
     43     {
     44         ASSERT_NOT_REACHED();
     45     }
     46 
     47     virtual bool isCurrentThread() const OVERRIDE
     48     {
     49         return true;
     50     }
     51 
     52     virtual blink::PlatformThreadId threadId() const OVERRIDE
     53     {
     54         ASSERT_NOT_REACHED();
     55         return 0;
     56     }
     57 
     58     virtual void enterRunLoop() OVERRIDE
     59     {
     60         ASSERT_NOT_REACHED();
     61     }
     62 
     63     virtual void exitRunLoop() OVERRIDE
     64     {
     65         ASSERT_NOT_REACHED();
     66     }
     67 
     68     void runPendingTasks()
     69     {
     70         while (!m_pendingTasks.isEmpty())
     71             m_pendingTasks.takeFirst()->run();
     72     }
     73 
     74     size_t numPendingMainThreadTasks() const
     75     {
     76         return m_pendingTasks.size();
     77     }
     78 
     79 private:
     80     WTF::Deque<OwnPtr<Task> > m_pendingTasks;
     81 };
     82 
     83 class SchedulerTestingPlatformSupport : blink::TestingPlatformSupport {
     84 public:
     85     SchedulerTestingPlatformSupport()
     86         : TestingPlatformSupport(TestingPlatformSupport::Config())
     87         , m_sharedTimerFunction(nullptr)
     88         , m_sharedTimerRunning(false)
     89         , m_sharedTimerFireInterval(0)
     90         , m_monotonicallyIncreasingTime(0)
     91     {
     92     }
     93 
     94     // blink::Platform implementation.
     95     virtual blink::WebThread* currentThread() OVERRIDE
     96     {
     97         return &m_mainThread;
     98     }
     99 
    100     virtual void setSharedTimerFiredFunction(SharedTimerFunction timerFunction) OVERRIDE
    101     {
    102         m_sharedTimerFunction = timerFunction;
    103     }
    104 
    105     virtual double monotonicallyIncreasingTime() OVERRIDE
    106     {
    107         return m_monotonicallyIncreasingTime;
    108     }
    109 
    110     virtual void setSharedTimerFireInterval(double)
    111     {
    112         m_sharedTimerFireInterval = 0;
    113         m_sharedTimerRunning = true;
    114     }
    115 
    116     virtual void stopSharedTimer()
    117     {
    118         m_sharedTimerRunning = false;
    119     }
    120 
    121     void runPendingTasks()
    122     {
    123         m_mainThread.runPendingTasks();
    124     }
    125 
    126     bool sharedTimerRunning() const
    127     {
    128         return m_sharedTimerRunning;
    129     }
    130 
    131     double sharedTimerFireInterval() const
    132     {
    133         return m_sharedTimerFireInterval;
    134     }
    135 
    136     void triggerSharedTimer()
    137     {
    138         m_sharedTimerFunction();
    139     }
    140 
    141     size_t numPendingMainThreadTasks() const
    142     {
    143         return m_mainThread.numPendingMainThreadTasks();
    144     }
    145 
    146     void setMonotonicTimeForTest(double time)
    147     {
    148         m_monotonicallyIncreasingTime = time;
    149     }
    150 
    151 private:
    152     TestMainThread m_mainThread;
    153     SharedTimerFunction m_sharedTimerFunction;
    154     bool m_sharedTimerRunning;
    155     double m_sharedTimerFireInterval;
    156     double m_monotonicallyIncreasingTime;
    157 };
    158 
    159 class SchedulerTest : public testing::Test {
    160 public:
    161     SchedulerTest()
    162         : m_reentrantCount(0)
    163         , m_maxRecursion(4)
    164     {
    165         SchedulerForTest::initializeOnMainThread();
    166         m_scheduler = static_cast<SchedulerForTest*>(Scheduler::shared());
    167     }
    168 
    169     ~SchedulerTest()
    170     {
    171         Scheduler::shutdown();
    172     }
    173 
    174     virtual void SetUp() OVERRIDE
    175     {
    176         m_scheduler->enterSchedulerPolicy(SchedulerForTest::Normal);
    177     }
    178 
    179     virtual void TearDown() OVERRIDE
    180     {
    181         // If the Scheduler hasn't been shut down then explicitly flush the tasks.
    182         if (Scheduler::shared())
    183             runPendingTasks();
    184     }
    185 
    186     void runPendingTasks()
    187     {
    188         m_platformSupport.runPendingTasks();
    189     }
    190 
    191     void appendToVector(std::string value)
    192     {
    193         m_order.push_back(value);
    194     }
    195 
    196     void appendToVectorReentrantTask()
    197     {
    198         m_reentrantOrder.push_back(m_reentrantCount++);
    199 
    200         if (m_reentrantCount > m_maxRecursion)
    201             return;
    202         Scheduler::shared()->postTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVectorReentrantTask, this));
    203     }
    204 
    205     void appendToVectorReentrantInputTask()
    206     {
    207         m_reentrantOrder.push_back(m_reentrantCount++);
    208 
    209         if (m_reentrantCount > m_maxRecursion)
    210             return;
    211         m_scheduler->postInputTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVectorReentrantInputTask, this));
    212     }
    213 
    214     void appendToVectorReentrantCompositorTask()
    215     {
    216         m_reentrantOrder.push_back(m_reentrantCount++);
    217 
    218         if (m_reentrantCount > m_maxRecursion)
    219             return;
    220         m_scheduler->postCompositorTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVectorReentrantCompositorTask, this));
    221     }
    222 
    223 protected:
    224     SchedulerTestingPlatformSupport m_platformSupport;
    225     SchedulerForTest* m_scheduler;
    226     std::vector<std::string> m_order;
    227     std::vector<int> m_reentrantOrder;
    228     int m_reentrantCount;
    229     int m_maxRecursion;
    230 };
    231 
    232 void orderedTestTask(int value, int* result)
    233 {
    234     *result = (*result << 4) | value;
    235 }
    236 
    237 void unorderedTestTask(int value, int* result)
    238 {
    239     *result += value;
    240 }
    241 
    242 void idleTestTask(int value, int* result, double allottedTime)
    243 {
    244     *result += value;
    245 }
    246 
    247 TEST_F(SchedulerTest, TestPostTask)
    248 {
    249     int result = 0;
    250     m_scheduler->postTask(FROM_HERE, WTF::bind(&orderedTestTask, 1, &result));
    251     m_scheduler->postTask(FROM_HERE, WTF::bind(&orderedTestTask, 2, &result));
    252     m_scheduler->postTask(FROM_HERE, WTF::bind(&orderedTestTask, 3, &result));
    253     m_scheduler->postTask(FROM_HERE, WTF::bind(&orderedTestTask, 4, &result));
    254     runPendingTasks();
    255     EXPECT_EQ(0x1234, result);
    256 }
    257 
    258 TEST_F(SchedulerTest, TestPostMixedTaskTypes)
    259 {
    260     int result = 0;
    261     m_scheduler->postTask(FROM_HERE, WTF::bind(&unorderedTestTask, 1, &result));
    262     m_scheduler->postInputTask(FROM_HERE, WTF::bind(&unorderedTestTask, 2, &result));
    263     m_scheduler->postCompositorTask(FROM_HERE, WTF::bind(&unorderedTestTask, 4, &result));
    264     m_scheduler->postTask(FROM_HERE, WTF::bind(&unorderedTestTask, 8, &result));
    265     m_scheduler->postIpcTask(FROM_HERE, WTF::bind(&unorderedTestTask, 16, &result));
    266     runPendingTasks();
    267     EXPECT_EQ(31, result);
    268 }
    269 
    270 int s_sharedTimerTickCount;
    271 void sharedTimerFunction()
    272 {
    273     s_sharedTimerTickCount++;
    274 }
    275 
    276 TEST_F(SchedulerTest, TestSharedTimer)
    277 {
    278     s_sharedTimerTickCount = 0;
    279     m_scheduler->setSharedTimerFiredFunction(&sharedTimerFunction);
    280     EXPECT_FALSE(m_platformSupport.sharedTimerRunning());
    281     m_scheduler->setSharedTimerFireInterval(0);
    282     EXPECT_TRUE(m_platformSupport.sharedTimerRunning());
    283 
    284     m_platformSupport.triggerSharedTimer();
    285     EXPECT_EQ(1, s_sharedTimerTickCount);
    286 
    287     m_scheduler->stopSharedTimer();
    288     EXPECT_FALSE(m_platformSupport.sharedTimerRunning());
    289 
    290     m_scheduler->setSharedTimerFiredFunction(nullptr);
    291     EXPECT_FALSE(m_platformSupport.sharedTimerRunning());
    292 }
    293 
    294 TEST_F(SchedulerTest, TestIdleTask)
    295 {
    296     // TODO: Check task allottedTime when implemented in the scheduler.
    297     int result = 0;
    298     m_scheduler->postIdleTask(FROM_HERE, WTF::bind<double>(&idleTestTask, 1, &result));
    299     m_scheduler->postIdleTask(FROM_HERE, WTF::bind<double>(&idleTestTask, 1, &result));
    300     m_scheduler->postIdleTask(FROM_HERE, WTF::bind<double>(&idleTestTask, 1, &result));
    301     m_scheduler->postIdleTask(FROM_HERE, WTF::bind<double>(&idleTestTask, 1, &result));
    302     runPendingTasks();
    303     EXPECT_EQ(4, result);
    304 }
    305 
    306 TEST_F(SchedulerTest, TestTaskPrioritization_normalPolicy)
    307 {
    308     m_scheduler->postTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVector, this, std::string("L1")));
    309     m_scheduler->postTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVector, this, std::string("L2")));
    310     m_scheduler->postInputTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVector, this, std::string("I1")));
    311     m_scheduler->postCompositorTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVector, this, std::string("C1")));
    312     m_scheduler->postInputTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVector, this, std::string("I2")));
    313     m_scheduler->postCompositorTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVector, this, std::string("C2")));
    314     m_scheduler->postIpcTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVector, this, std::string("IPC")));
    315 
    316     runPendingTasks();
    317     EXPECT_THAT(m_order, testing::ElementsAre(
    318         std::string("L1"), std::string("L2"), std::string("I1"), std::string("C1"), std::string("I2"), std::string("C2"),
    319         std::string("IPC")));
    320 }
    321 
    322 TEST_F(SchedulerTest, TestTaskPrioritization_compositorPriorityPolicy)
    323 {
    324     m_scheduler->enterSchedulerPolicy(SchedulerForTest::CompositorPriority);
    325     m_scheduler->postTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVector, this, std::string("L1")));
    326     m_scheduler->postTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVector, this, std::string("L2")));
    327     m_scheduler->postInputTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVector, this, std::string("I1")));
    328     m_scheduler->postCompositorTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVector, this, std::string("C1")));
    329     m_scheduler->postInputTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVector, this, std::string("I2")));
    330     m_scheduler->postCompositorTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVector, this, std::string("C2")));
    331     m_scheduler->postIpcTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVector, this, std::string("IPC")));
    332 
    333     runPendingTasks();
    334     EXPECT_THAT(m_order, testing::ElementsAre(
    335         std::string("I1"), std::string("C1"), std::string("I2"), std::string("C2"), std::string("L1"), std::string("L2"),
    336         std::string("IPC")));
    337 }
    338 
    339 TEST_F(SchedulerTest, TestRentrantTask)
    340 {
    341     m_scheduler->postTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVectorReentrantTask, this));
    342     runPendingTasks();
    343 
    344     EXPECT_THAT(m_reentrantOrder, testing::ElementsAre(0, 1, 2, 3, 4));
    345 }
    346 
    347 
    348 TEST_F(SchedulerTest, TestRentrantInputTaskDuringShutdown)
    349 {
    350     m_scheduler->postInputTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVectorReentrantInputTask, this));
    351     Scheduler::shutdown();
    352 
    353     EXPECT_THAT(m_reentrantOrder, testing::ElementsAre(0, 1, 2, 3, 4));
    354 }
    355 
    356 TEST_F(SchedulerTest, TestRentrantCompositorTaskDuringShutdown)
    357 {
    358     m_scheduler->postCompositorTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVectorReentrantCompositorTask, this));
    359     Scheduler::shutdown();
    360 
    361     EXPECT_THAT(m_reentrantOrder, testing::ElementsAre(0, 1, 2, 3, 4));
    362 }
    363 
    364 bool s_shouldContinue;
    365 void reentrantInputTask(Scheduler* scheduler)
    366 {
    367     if (s_shouldContinue)
    368         scheduler->postInputTask(FROM_HERE, WTF::bind(&reentrantInputTask, scheduler));
    369 }
    370 
    371 void reentrantCompositorTask(Scheduler* scheduler)
    372 {
    373     if (s_shouldContinue)
    374         scheduler->postCompositorTask(FROM_HERE, WTF::bind(&reentrantCompositorTask, scheduler));
    375 }
    376 
    377 void stopReentrantTask()
    378 {
    379     s_shouldContinue = false;
    380 }
    381 
    382 TEST_F(SchedulerTest, TestRentrantInputTaskDoesNotStarveOutLowPriorityTask)
    383 {
    384     s_shouldContinue = true;
    385     m_scheduler->postInputTask(FROM_HERE, WTF::bind(&reentrantInputTask, m_scheduler));
    386     m_scheduler->postTask(FROM_HERE, WTF::bind(&stopReentrantTask));
    387 
    388     // If starvation occurs then this will never exit.
    389     runPendingTasks();
    390 }
    391 
    392 TEST_F(SchedulerTest, TestRentrantCompositorTaskDoesNotStarveOutLowPriorityTask)
    393 {
    394     s_shouldContinue = true;
    395     m_scheduler->postInputTask(FROM_HERE, WTF::bind(&reentrantCompositorTask, m_scheduler));
    396     m_scheduler->postTask(FROM_HERE, WTF::bind(&stopReentrantTask));
    397 
    398     // If starvation occurs then this will never exit.
    399     runPendingTasks();
    400 }
    401 
    402 int s_dummyTaskCount;
    403 void dummyTask()
    404 {
    405     s_dummyTaskCount++;
    406 }
    407 
    408 TEST_F(SchedulerTest, TestMultipleCallsToPostInputOrCompositorTaskResultsInOnlyOneMainThreadTask)
    409 {
    410     EXPECT_EQ(0U, m_platformSupport.numPendingMainThreadTasks());
    411 
    412     for (int i = 0; i < 10; i++) {
    413         m_scheduler->postInputTask(FROM_HERE, WTF::bind(&dummyTask));
    414         m_scheduler->postCompositorTask(FROM_HERE, WTF::bind(&dummyTask));
    415     }
    416 
    417     EXPECT_EQ(1U, m_platformSupport.numPendingMainThreadTasks());
    418 }
    419 
    420 TEST_F(SchedulerTest, TestMainThreadTaskLifeCycle)
    421 {
    422     EXPECT_EQ(0U, m_platformSupport.numPendingMainThreadTasks());
    423 
    424     m_scheduler->postInputTask(FROM_HERE, WTF::bind(&dummyTask));
    425     EXPECT_EQ(1U, m_platformSupport.numPendingMainThreadTasks());
    426 
    427     runPendingTasks();
    428     EXPECT_EQ(0U, m_platformSupport.numPendingMainThreadTasks());
    429 
    430     m_scheduler->postCompositorTask(FROM_HERE, WTF::bind(&dummyTask));
    431     EXPECT_EQ(1U, m_platformSupport.numPendingMainThreadTasks());
    432 
    433     runPendingTasks();
    434     EXPECT_EQ(0U, m_platformSupport.numPendingMainThreadTasks());
    435 }
    436 
    437 void postDummyInputTask()
    438 {
    439     Scheduler::shared()->postInputTask(FROM_HERE, WTF::bind(&dummyTask));
    440 }
    441 
    442 TEST_F(SchedulerTest, HighPriorityTasksOnlyDontRunBecauseOfSharedTimerFiring_InNormalMode)
    443 {
    444     s_dummyTaskCount = 0;
    445     m_scheduler->postInputTask(FROM_HERE, WTF::bind(&dummyTask));
    446     // Trigger the posting of an input task during execution of the shared timer function.
    447     m_scheduler->setSharedTimerFiredFunction(&postDummyInputTask);
    448     m_scheduler->setSharedTimerFireInterval(0);
    449     m_platformSupport.triggerSharedTimer();
    450 
    451     EXPECT_EQ(0, s_dummyTaskCount);
    452 
    453     // Clean up.
    454     m_scheduler->stopSharedTimer();
    455     m_scheduler->setSharedTimerFiredFunction(nullptr);
    456 }
    457 
    458 TEST_F(SchedulerTest, HighPriorityTasksOnlyRunOncePerSharedTimerFiring_InLowSchedulerPolicy)
    459 {
    460     s_dummyTaskCount = 0;
    461     m_scheduler->enterSchedulerPolicy(SchedulerForTest::CompositorPriority);
    462     m_scheduler->postInputTask(FROM_HERE, WTF::bind(&dummyTask));
    463     // Trigger the posting of an input task during execution of the shared timer function.
    464     m_scheduler->setSharedTimerFiredFunction(&postDummyInputTask);
    465     m_scheduler->setSharedTimerFireInterval(0);
    466     m_platformSupport.triggerSharedTimer();
    467 
    468     EXPECT_EQ(1, s_dummyTaskCount);
    469 
    470     // Clean up.
    471     m_scheduler->stopSharedTimer();
    472     m_scheduler->setSharedTimerFiredFunction(nullptr);
    473 }
    474 
    475 TEST_F(SchedulerTest, TestInputEventDoesNotTriggerShouldYield_InNormalMode)
    476 {
    477     m_scheduler->postInputTask(FROM_HERE, WTF::bind(&dummyTask));
    478 
    479     EXPECT_FALSE(m_scheduler->shouldYieldForHighPriorityWork());
    480 }
    481 
    482 TEST_F(SchedulerTest, TestDidReceiveInputEventDoesNotTriggerShouldYield)
    483 {
    484     m_scheduler->didReceiveInputEvent();
    485 
    486     EXPECT_FALSE(m_scheduler->shouldYieldForHighPriorityWork());
    487 }
    488 
    489 TEST_F(SchedulerTest, TestCompositorTaskDoesNotTriggerShouldYield_InNormalMode)
    490 {
    491     m_scheduler->postCompositorTask(FROM_HERE, WTF::bind(&dummyTask));
    492 
    493     EXPECT_FALSE(m_scheduler->shouldYieldForHighPriorityWork());
    494 }
    495 
    496 TEST_F(SchedulerTest, TestIpcTaskDoesNotTriggerShouldYield_InNormalMode)
    497 {
    498     m_scheduler->postIpcTask(FROM_HERE, WTF::bind(&dummyTask));
    499 
    500     EXPECT_FALSE(m_scheduler->shouldYieldForHighPriorityWork());
    501 }
    502 
    503 TEST_F(SchedulerTest, TestCompositorTaskDoesTriggerShouldYieldAfterDidReceiveInputEvent)
    504 {
    505     m_scheduler->didReceiveInputEvent();
    506 
    507     ASSERT_FALSE(m_scheduler->shouldYieldForHighPriorityWork());
    508     m_scheduler->postCompositorTask(FROM_HERE, WTF::bind(&dummyTask));
    509 
    510     EXPECT_TRUE(m_scheduler->shouldYieldForHighPriorityWork());
    511 }
    512 
    513 TEST_F(SchedulerTest, TestInputTaskDoesTriggerShouldYield_InCompositorPriorityMode)
    514 {
    515     m_scheduler->enterSchedulerPolicy(SchedulerForTest::CompositorPriority);
    516     m_scheduler->postInputTask(FROM_HERE, WTF::bind(&dummyTask));
    517 
    518     EXPECT_TRUE(m_scheduler->shouldYieldForHighPriorityWork());
    519 }
    520 
    521 
    522 TEST_F(SchedulerTest, TestCompositorTaskDoesTriggerShouldYield_InCompositorPriorityMode)
    523 {
    524     m_scheduler->enterSchedulerPolicy(SchedulerForTest::CompositorPriority);
    525     m_scheduler->postCompositorTask(FROM_HERE, WTF::bind(&dummyTask));
    526 
    527     EXPECT_TRUE(m_scheduler->shouldYieldForHighPriorityWork());
    528 }
    529 
    530 TEST_F(SchedulerTest, TestIpcTaskDoesNotTriggerShouldYield_InCompositorPriorityMode)
    531 {
    532     m_scheduler->enterSchedulerPolicy(SchedulerForTest::CompositorPriority);
    533     m_scheduler->postIpcTask(FROM_HERE, WTF::bind(&dummyTask));
    534 
    535     EXPECT_FALSE(m_scheduler->shouldYieldForHighPriorityWork());
    536 }
    537 
    538 TEST_F(SchedulerTest, testDidReceiveInputEvent_DoesntTriggerLowLatencyModeForLong)
    539 {
    540     m_platformSupport.setMonotonicTimeForTest(1000.0);
    541 
    542     // Note the latency mode gets reset by executeHighPriorityTasks, so we need a dummy task here
    543     // to make sure runPendingTasks triggers executeHighPriorityTasks.
    544     m_scheduler->postCompositorTask(FROM_HERE, WTF::bind(&dummyTask));
    545     m_scheduler->didReceiveInputEvent();
    546     m_platformSupport.setMonotonicTimeForTest(1000.5);
    547     runPendingTasks();
    548 
    549     ASSERT_FALSE(m_scheduler->shouldYieldForHighPriorityWork());
    550     m_scheduler->postCompositorTask(FROM_HERE, WTF::bind(&dummyTask));
    551 
    552     EXPECT_FALSE(m_scheduler->shouldYieldForHighPriorityWork());
    553 }
    554 
    555 } // namespace
    556