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