#include <vector>
using blink::Scheduler;
-using namespace std;
namespace {
+class SchedulerForTest : public blink::Scheduler {
+public:
+ static void initializeOnMainThread()
+ {
+ s_sharedScheduler = new SchedulerForTest();
+ }
+
+ using Scheduler::Normal;
+ using Scheduler::CompositorPriority;
+ using Scheduler::enterSchedulerPolicy;
+};
+
class TestMainThread : public blink::WebThread {
public:
// blink::WebThread implementation.
return true;
}
+ virtual blink::PlatformThreadId threadId() const OVERRIDE
+ {
+ ASSERT_NOT_REACHED();
+ return 0;
+ }
+
virtual void enterRunLoop() OVERRIDE
{
ASSERT_NOT_REACHED();
m_pendingTasks.takeFirst()->run();
}
+ size_t numPendingMainThreadTasks() const
+ {
+ return m_pendingTasks.size();
+ }
+
private:
WTF::Deque<OwnPtr<Task> > m_pendingTasks;
};
, m_sharedTimerFunction(nullptr)
, m_sharedTimerRunning(false)
, m_sharedTimerFireInterval(0)
+ , m_monotonicallyIncreasingTime(0)
{
}
m_sharedTimerFunction = timerFunction;
}
+ virtual double monotonicallyIncreasingTime() OVERRIDE
+ {
+ return m_monotonicallyIncreasingTime;
+ }
+
virtual void setSharedTimerFireInterval(double)
{
m_sharedTimerFireInterval = 0;
m_sharedTimerFunction();
}
+ size_t numPendingMainThreadTasks() const
+ {
+ return m_mainThread.numPendingMainThreadTasks();
+ }
+
+ void setMonotonicTimeForTest(double time)
+ {
+ m_monotonicallyIncreasingTime = time;
+ }
+
private:
TestMainThread m_mainThread;
SharedTimerFunction m_sharedTimerFunction;
bool m_sharedTimerRunning;
double m_sharedTimerFireInterval;
+ double m_monotonicallyIncreasingTime;
};
class SchedulerTest : public testing::Test {
: m_reentrantCount(0)
, m_maxRecursion(4)
{
- Scheduler::initializeOnMainThread();
- m_scheduler = Scheduler::shared();
+ SchedulerForTest::initializeOnMainThread();
+ m_scheduler = static_cast<SchedulerForTest*>(Scheduler::shared());
}
~SchedulerTest()
Scheduler::shutdown();
}
+ virtual void SetUp() OVERRIDE
+ {
+ m_scheduler->enterSchedulerPolicy(SchedulerForTest::Normal);
+ }
+
+ virtual void TearDown() OVERRIDE
+ {
+ // If the Scheduler hasn't been shut down then explicitly flush the tasks.
+ if (Scheduler::shared())
+ runPendingTasks();
+ }
+
void runPendingTasks()
{
m_platformSupport.runPendingTasks();
}
- void appendToVector(string value)
+ void appendToVector(std::string value)
{
m_order.push_back(value);
}
protected:
SchedulerTestingPlatformSupport m_platformSupport;
- Scheduler* m_scheduler;
- std::vector<string> m_order;
+ SchedulerForTest* m_scheduler;
+ std::vector<std::string> m_order;
std::vector<int> m_reentrantOrder;
int m_reentrantCount;
int m_maxRecursion;
m_scheduler->postInputTask(FROM_HERE, WTF::bind(&unorderedTestTask, 2, &result));
m_scheduler->postCompositorTask(FROM_HERE, WTF::bind(&unorderedTestTask, 4, &result));
m_scheduler->postTask(FROM_HERE, WTF::bind(&unorderedTestTask, 8, &result));
+ m_scheduler->postIpcTask(FROM_HERE, WTF::bind(&unorderedTestTask, 16, &result));
runPendingTasks();
- EXPECT_EQ(15, result);
+ EXPECT_EQ(31, result);
}
int s_sharedTimerTickCount;
EXPECT_EQ(4, result);
}
-TEST_F(SchedulerTest, TestTaskPrioritization)
+TEST_F(SchedulerTest, TestTaskPrioritization_normalPolicy)
+{
+ m_scheduler->postTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVector, this, std::string("L1")));
+ m_scheduler->postTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVector, this, std::string("L2")));
+ m_scheduler->postInputTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVector, this, std::string("I1")));
+ m_scheduler->postCompositorTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVector, this, std::string("C1")));
+ m_scheduler->postInputTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVector, this, std::string("I2")));
+ m_scheduler->postCompositorTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVector, this, std::string("C2")));
+ m_scheduler->postIpcTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVector, this, std::string("IPC")));
+
+ runPendingTasks();
+ EXPECT_THAT(m_order, testing::ElementsAre(
+ std::string("L1"), std::string("L2"), std::string("I1"), std::string("C1"), std::string("I2"), std::string("C2"),
+ std::string("IPC")));
+}
+
+TEST_F(SchedulerTest, TestTaskPrioritization_compositorPriorityPolicy)
{
- m_scheduler->postTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVector, this, string("L1")));
- m_scheduler->postTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVector, this, string("L2")));
- m_scheduler->postInputTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVector, this, string("I1")));
- m_scheduler->postInputTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVector, this, string("I2")));
- m_scheduler->postCompositorTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVector, this, string("C1")));
- m_scheduler->postCompositorTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVector, this, string("C2")));
+ m_scheduler->enterSchedulerPolicy(SchedulerForTest::CompositorPriority);
+ m_scheduler->postTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVector, this, std::string("L1")));
+ m_scheduler->postTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVector, this, std::string("L2")));
+ m_scheduler->postInputTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVector, this, std::string("I1")));
+ m_scheduler->postCompositorTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVector, this, std::string("C1")));
+ m_scheduler->postInputTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVector, this, std::string("I2")));
+ m_scheduler->postCompositorTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVector, this, std::string("C2")));
+ m_scheduler->postIpcTask(FROM_HERE, WTF::bind(&SchedulerTest::appendToVector, this, std::string("IPC")));
runPendingTasks();
EXPECT_THAT(m_order, testing::ElementsAre(
- string("I1"), string("I2"), string("C1"), string("C2"), string("L1"), string("L2")));
+ std::string("I1"), std::string("C1"), std::string("I2"), std::string("C2"), std::string("L1"), std::string("L2"),
+ std::string("IPC")));
}
TEST_F(SchedulerTest, TestRentrantTask)
EXPECT_THAT(m_reentrantOrder, testing::ElementsAre(0, 1, 2, 3, 4));
}
+bool s_shouldContinue;
+void reentrantInputTask(Scheduler* scheduler)
+{
+ if (s_shouldContinue)
+ scheduler->postInputTask(FROM_HERE, WTF::bind(&reentrantInputTask, scheduler));
+}
+
+void reentrantCompositorTask(Scheduler* scheduler)
+{
+ if (s_shouldContinue)
+ scheduler->postCompositorTask(FROM_HERE, WTF::bind(&reentrantCompositorTask, scheduler));
+}
+
+void stopReentrantTask()
+{
+ s_shouldContinue = false;
+}
+
+TEST_F(SchedulerTest, TestRentrantInputTaskDoesNotStarveOutLowPriorityTask)
+{
+ s_shouldContinue = true;
+ m_scheduler->postInputTask(FROM_HERE, WTF::bind(&reentrantInputTask, m_scheduler));
+ m_scheduler->postTask(FROM_HERE, WTF::bind(&stopReentrantTask));
+
+ // If starvation occurs then this will never exit.
+ runPendingTasks();
+}
+
+TEST_F(SchedulerTest, TestRentrantCompositorTaskDoesNotStarveOutLowPriorityTask)
+{
+ s_shouldContinue = true;
+ m_scheduler->postInputTask(FROM_HERE, WTF::bind(&reentrantCompositorTask, m_scheduler));
+ m_scheduler->postTask(FROM_HERE, WTF::bind(&stopReentrantTask));
+
+ // If starvation occurs then this will never exit.
+ runPendingTasks();
+}
+
+int s_dummyTaskCount;
+void dummyTask()
+{
+ s_dummyTaskCount++;
+}
+
+TEST_F(SchedulerTest, TestMultipleCallsToPostInputOrCompositorTaskResultsInOnlyOneMainThreadTask)
+{
+ EXPECT_EQ(0U, m_platformSupport.numPendingMainThreadTasks());
+
+ for (int i = 0; i < 10; i++) {
+ m_scheduler->postInputTask(FROM_HERE, WTF::bind(&dummyTask));
+ m_scheduler->postCompositorTask(FROM_HERE, WTF::bind(&dummyTask));
+ }
+
+ EXPECT_EQ(1U, m_platformSupport.numPendingMainThreadTasks());
+}
+
+TEST_F(SchedulerTest, TestMainThreadTaskLifeCycle)
+{
+ EXPECT_EQ(0U, m_platformSupport.numPendingMainThreadTasks());
+
+ m_scheduler->postInputTask(FROM_HERE, WTF::bind(&dummyTask));
+ EXPECT_EQ(1U, m_platformSupport.numPendingMainThreadTasks());
+
+ runPendingTasks();
+ EXPECT_EQ(0U, m_platformSupport.numPendingMainThreadTasks());
+
+ m_scheduler->postCompositorTask(FROM_HERE, WTF::bind(&dummyTask));
+ EXPECT_EQ(1U, m_platformSupport.numPendingMainThreadTasks());
+
+ runPendingTasks();
+ EXPECT_EQ(0U, m_platformSupport.numPendingMainThreadTasks());
+}
+
+void postDummyInputTask()
+{
+ Scheduler::shared()->postInputTask(FROM_HERE, WTF::bind(&dummyTask));
+}
+
+TEST_F(SchedulerTest, HighPriorityTasksOnlyDontRunBecauseOfSharedTimerFiring_InNormalMode)
+{
+ s_dummyTaskCount = 0;
+ m_scheduler->postInputTask(FROM_HERE, WTF::bind(&dummyTask));
+ // Trigger the posting of an input task during execution of the shared timer function.
+ m_scheduler->setSharedTimerFiredFunction(&postDummyInputTask);
+ m_scheduler->setSharedTimerFireInterval(0);
+ m_platformSupport.triggerSharedTimer();
+
+ EXPECT_EQ(0, s_dummyTaskCount);
+
+ // Clean up.
+ m_scheduler->stopSharedTimer();
+ m_scheduler->setSharedTimerFiredFunction(nullptr);
+}
+
+TEST_F(SchedulerTest, HighPriorityTasksOnlyRunOncePerSharedTimerFiring_InLowSchedulerPolicy)
+{
+ s_dummyTaskCount = 0;
+ m_scheduler->enterSchedulerPolicy(SchedulerForTest::CompositorPriority);
+ m_scheduler->postInputTask(FROM_HERE, WTF::bind(&dummyTask));
+ // Trigger the posting of an input task during execution of the shared timer function.
+ m_scheduler->setSharedTimerFiredFunction(&postDummyInputTask);
+ m_scheduler->setSharedTimerFireInterval(0);
+ m_platformSupport.triggerSharedTimer();
+
+ EXPECT_EQ(1, s_dummyTaskCount);
+
+ // Clean up.
+ m_scheduler->stopSharedTimer();
+ m_scheduler->setSharedTimerFiredFunction(nullptr);
+}
+
+TEST_F(SchedulerTest, TestInputEventDoesNotTriggerShouldYield_InNormalMode)
+{
+ m_scheduler->postInputTask(FROM_HERE, WTF::bind(&dummyTask));
+
+ EXPECT_FALSE(m_scheduler->shouldYieldForHighPriorityWork());
+}
+
+TEST_F(SchedulerTest, TestDidReceiveInputEventDoesNotTriggerShouldYield)
+{
+ m_scheduler->didReceiveInputEvent();
+
+ EXPECT_FALSE(m_scheduler->shouldYieldForHighPriorityWork());
+}
+
+TEST_F(SchedulerTest, TestCompositorTaskDoesNotTriggerShouldYield_InNormalMode)
+{
+ m_scheduler->postCompositorTask(FROM_HERE, WTF::bind(&dummyTask));
+
+ EXPECT_FALSE(m_scheduler->shouldYieldForHighPriorityWork());
+}
+
+TEST_F(SchedulerTest, TestIpcTaskDoesNotTriggerShouldYield_InNormalMode)
+{
+ m_scheduler->postIpcTask(FROM_HERE, WTF::bind(&dummyTask));
+
+ EXPECT_FALSE(m_scheduler->shouldYieldForHighPriorityWork());
+}
+
+TEST_F(SchedulerTest, TestCompositorTaskDoesTriggerShouldYieldAfterDidReceiveInputEvent)
+{
+ m_scheduler->didReceiveInputEvent();
+
+ ASSERT_FALSE(m_scheduler->shouldYieldForHighPriorityWork());
+ m_scheduler->postCompositorTask(FROM_HERE, WTF::bind(&dummyTask));
+
+ EXPECT_TRUE(m_scheduler->shouldYieldForHighPriorityWork());
+}
+
+TEST_F(SchedulerTest, TestInputTaskDoesTriggerShouldYield_InCompositorPriorityMode)
+{
+ m_scheduler->enterSchedulerPolicy(SchedulerForTest::CompositorPriority);
+ m_scheduler->postInputTask(FROM_HERE, WTF::bind(&dummyTask));
+
+ EXPECT_TRUE(m_scheduler->shouldYieldForHighPriorityWork());
+}
+
+
+TEST_F(SchedulerTest, TestCompositorTaskDoesTriggerShouldYield_InCompositorPriorityMode)
+{
+ m_scheduler->enterSchedulerPolicy(SchedulerForTest::CompositorPriority);
+ m_scheduler->postCompositorTask(FROM_HERE, WTF::bind(&dummyTask));
+
+ EXPECT_TRUE(m_scheduler->shouldYieldForHighPriorityWork());
+}
+
+TEST_F(SchedulerTest, TestIpcTaskDoesNotTriggerShouldYield_InCompositorPriorityMode)
+{
+ m_scheduler->enterSchedulerPolicy(SchedulerForTest::CompositorPriority);
+ m_scheduler->postIpcTask(FROM_HERE, WTF::bind(&dummyTask));
+
+ EXPECT_FALSE(m_scheduler->shouldYieldForHighPriorityWork());
+}
+
+TEST_F(SchedulerTest, testDidReceiveInputEvent_DoesntTriggerLowLatencyModeForLong)
+{
+ m_platformSupport.setMonotonicTimeForTest(1000.0);
+
+ // Note the latency mode gets reset by executeHighPriorityTasks, so we need a dummy task here
+ // to make sure runPendingTasks triggers executeHighPriorityTasks.
+ m_scheduler->postCompositorTask(FROM_HERE, WTF::bind(&dummyTask));
+ m_scheduler->didReceiveInputEvent();
+ m_platformSupport.setMonotonicTimeForTest(1000.5);
+ runPendingTasks();
+
+ ASSERT_FALSE(m_scheduler->shouldYieldForHighPriorityWork());
+ m_scheduler->postCompositorTask(FROM_HERE, WTF::bind(&dummyTask));
+
+ EXPECT_FALSE(m_scheduler->shouldYieldForHighPriorityWork());
+}
+
} // namespace