Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/task_scheduler/task_tracker.h" | 5 #include "base/task_scheduler/task_tracker.h" |
| 6 | 6 |
| 7 #include <stdint.h> | 7 #include <stdint.h> |
| 8 | 8 |
| 9 #include <memory> | 9 #include <memory> |
| 10 #include <vector> | 10 #include <vector> |
| 11 | 11 |
| 12 #include "base/bind.h" | 12 #include "base/bind.h" |
| 13 #include "base/callback.h" | |
| 13 #include "base/logging.h" | 14 #include "base/logging.h" |
| 14 #include "base/macros.h" | 15 #include "base/macros.h" |
| 15 #include "base/memory/ptr_util.h" | 16 #include "base/memory/ptr_util.h" |
| 16 #include "base/memory/ref_counted.h" | 17 #include "base/memory/ref_counted.h" |
| 17 #include "base/sequence_token.h" | 18 #include "base/sequence_token.h" |
| 18 #include "base/sequenced_task_runner.h" | 19 #include "base/sequenced_task_runner.h" |
| 19 #include "base/single_thread_task_runner.h" | 20 #include "base/single_thread_task_runner.h" |
| 21 #include "base/synchronization/atomic_flag.h" | |
| 20 #include "base/synchronization/waitable_event.h" | 22 #include "base/synchronization/waitable_event.h" |
| 21 #include "base/task_scheduler/scheduler_lock.h" | 23 #include "base/task_scheduler/scheduler_lock.h" |
| 22 #include "base/task_scheduler/task.h" | 24 #include "base/task_scheduler/task.h" |
| 23 #include "base/task_scheduler/task_traits.h" | 25 #include "base/task_scheduler/task_traits.h" |
| 24 #include "base/test/gtest_util.h" | 26 #include "base/test/gtest_util.h" |
| 25 #include "base/test/test_simple_task_runner.h" | 27 #include "base/test/test_simple_task_runner.h" |
| 28 #include "base/test/test_timeouts.h" | |
| 26 #include "base/threading/platform_thread.h" | 29 #include "base/threading/platform_thread.h" |
| 27 #include "base/threading/sequenced_task_runner_handle.h" | 30 #include "base/threading/sequenced_task_runner_handle.h" |
| 28 #include "base/threading/simple_thread.h" | 31 #include "base/threading/simple_thread.h" |
| 29 #include "base/threading/thread_restrictions.h" | 32 #include "base/threading/thread_restrictions.h" |
| 30 #include "base/threading/thread_task_runner_handle.h" | 33 #include "base/threading/thread_task_runner_handle.h" |
| 31 #include "testing/gtest/include/gtest/gtest.h" | 34 #include "testing/gtest/include/gtest/gtest.h" |
| 32 | 35 |
| 33 namespace base { | 36 namespace base { |
| 34 namespace internal { | 37 namespace internal { |
| 35 | 38 |
| 36 namespace { | 39 namespace { |
| 37 | 40 |
| 38 constexpr size_t kLoadTestNumIterations = 100; | 41 constexpr size_t kLoadTestNumIterations = 100; |
| 39 | 42 |
| 40 // Calls TaskTracker::Shutdown() asynchronously. | 43 // Invokes a closure asynchronously. |
| 41 class ThreadCallingShutdown : public SimpleThread { | 44 class CallbackThread : public SimpleThread { |
| 42 public: | 45 public: |
| 43 explicit ThreadCallingShutdown(TaskTracker* tracker) | 46 explicit CallbackThread(const Closure& closure) |
| 44 : SimpleThread("ThreadCallingShutdown"), | 47 : SimpleThread("CallbackThread"), closure_(closure) {} |
| 45 tracker_(tracker), | |
| 46 has_returned_(WaitableEvent::ResetPolicy::MANUAL, | |
| 47 WaitableEvent::InitialState::NOT_SIGNALED) {} | |
| 48 | 48 |
| 49 // Returns true once the async call to Shutdown() has returned. | 49 // Returns true once the async call to Shutdown() has returned. |
| 50 bool has_returned() { return has_returned_.IsSignaled(); } | 50 bool has_returned() { return has_returned_.IsSet(); } |
| 51 | 51 |
| 52 private: | 52 private: |
| 53 void Run() override { | 53 void Run() override { |
| 54 tracker_->Shutdown(); | 54 closure_.Run(); |
| 55 has_returned_.Signal(); | 55 has_returned_.Set(); |
| 56 } | 56 } |
| 57 | 57 |
| 58 TaskTracker* const tracker_; | 58 const Closure closure_; |
| 59 WaitableEvent has_returned_; | 59 AtomicFlag has_returned_; |
| 60 | 60 |
| 61 DISALLOW_COPY_AND_ASSIGN(ThreadCallingShutdown); | 61 DISALLOW_COPY_AND_ASSIGN(CallbackThread); |
| 62 }; | 62 }; |
| 63 | 63 |
| 64 class ThreadPostingAndRunningTask : public SimpleThread { | 64 class ThreadPostingAndRunningTask : public SimpleThread { |
| 65 public: | 65 public: |
| 66 enum class Action { | 66 enum class Action { |
| 67 WILL_POST, | 67 WILL_POST, |
| 68 RUN, | 68 RUN, |
| 69 WILL_POST_AND_RUN, | 69 WILL_POST_AND_RUN, |
| 70 }; | 70 }; |
| 71 | 71 |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 124 FROM_HERE, | 124 FROM_HERE, |
| 125 Bind(&TaskSchedulerTaskTrackerTest::RunTaskCallback, Unretained(this)), | 125 Bind(&TaskSchedulerTaskTrackerTest::RunTaskCallback, Unretained(this)), |
| 126 TaskTraits().WithShutdownBehavior(shutdown_behavior), TimeDelta()); | 126 TaskTraits().WithShutdownBehavior(shutdown_behavior), TimeDelta()); |
| 127 } | 127 } |
| 128 | 128 |
| 129 // Calls tracker_->Shutdown() on a new thread. When this returns, Shutdown() | 129 // Calls tracker_->Shutdown() on a new thread. When this returns, Shutdown() |
| 130 // method has been entered on the new thread, but it hasn't necessarily | 130 // method has been entered on the new thread, but it hasn't necessarily |
| 131 // returned. | 131 // returned. |
| 132 void CallShutdownAsync() { | 132 void CallShutdownAsync() { |
| 133 ASSERT_FALSE(thread_calling_shutdown_); | 133 ASSERT_FALSE(thread_calling_shutdown_); |
| 134 thread_calling_shutdown_.reset(new ThreadCallingShutdown(&tracker_)); | 134 thread_calling_shutdown_.reset(new CallbackThread( |
| 135 Bind(&TaskTracker::Shutdown, Unretained(&tracker_)))); | |
| 135 thread_calling_shutdown_->Start(); | 136 thread_calling_shutdown_->Start(); |
| 136 while (!tracker_.HasShutdownStarted()) | 137 while (!tracker_.HasShutdownStarted()) |
| 137 PlatformThread::YieldCurrentThread(); | 138 PlatformThread::YieldCurrentThread(); |
| 138 } | 139 } |
| 139 | 140 |
| 140 void WaitForAsyncIsShutdownComplete() { | 141 void WaitForAsyncIsShutdownComplete() { |
| 141 ASSERT_TRUE(thread_calling_shutdown_); | 142 ASSERT_TRUE(thread_calling_shutdown_); |
| 142 thread_calling_shutdown_->Join(); | 143 thread_calling_shutdown_->Join(); |
| 143 EXPECT_TRUE(thread_calling_shutdown_->has_returned()); | 144 EXPECT_TRUE(thread_calling_shutdown_->has_returned()); |
| 144 EXPECT_TRUE(tracker_.IsShutdownComplete()); | 145 EXPECT_TRUE(tracker_.IsShutdownComplete()); |
| 145 } | 146 } |
| 146 | 147 |
| 147 void VerifyAsyncShutdownInProgress() { | 148 void VerifyAsyncShutdownInProgress() { |
| 148 ASSERT_TRUE(thread_calling_shutdown_); | 149 ASSERT_TRUE(thread_calling_shutdown_); |
| 149 EXPECT_FALSE(thread_calling_shutdown_->has_returned()); | 150 EXPECT_FALSE(thread_calling_shutdown_->has_returned()); |
| 150 EXPECT_TRUE(tracker_.HasShutdownStarted()); | 151 EXPECT_TRUE(tracker_.HasShutdownStarted()); |
| 151 EXPECT_FALSE(tracker_.IsShutdownComplete()); | 152 EXPECT_FALSE(tracker_.IsShutdownComplete()); |
| 152 } | 153 } |
| 153 | 154 |
| 155 // Calls tracker_->FlushForTesting() on a new thread. | |
| 156 void CallFlushForTestingAsync() { | |
| 157 ASSERT_FALSE(thread_calling_flush_for_testing_); | |
| 158 thread_calling_flush_for_testing_.reset(new CallbackThread( | |
| 159 Bind(&TaskTracker::FlushForTesting, Unretained(&tracker_)))); | |
| 160 thread_calling_flush_for_testing_->Start(); | |
| 161 } | |
| 162 | |
| 163 void WaitForAsyncFlushForTestingReturned() { | |
| 164 ASSERT_TRUE(thread_calling_flush_for_testing_); | |
| 165 thread_calling_flush_for_testing_->Join(); | |
| 166 EXPECT_TRUE(thread_calling_flush_for_testing_->has_returned()); | |
| 167 } | |
| 168 | |
| 169 void VerifyAsyncFlushForTestingInProgress() { | |
| 170 ASSERT_TRUE(thread_calling_flush_for_testing_); | |
| 171 EXPECT_FALSE(thread_calling_flush_for_testing_->has_returned()); | |
| 172 } | |
| 173 | |
| 154 size_t NumTasksExecuted() { | 174 size_t NumTasksExecuted() { |
| 155 AutoSchedulerLock auto_lock(lock_); | 175 AutoSchedulerLock auto_lock(lock_); |
| 156 return num_tasks_executed_; | 176 return num_tasks_executed_; |
| 157 } | 177 } |
| 158 | 178 |
| 159 TaskTracker tracker_; | 179 TaskTracker tracker_; |
| 160 | 180 |
| 161 private: | 181 private: |
| 162 void RunTaskCallback() { | 182 void RunTaskCallback() { |
| 163 AutoSchedulerLock auto_lock(lock_); | 183 AutoSchedulerLock auto_lock(lock_); |
| 164 ++num_tasks_executed_; | 184 ++num_tasks_executed_; |
| 165 } | 185 } |
| 166 | 186 |
| 167 std::unique_ptr<ThreadCallingShutdown> thread_calling_shutdown_; | 187 std::unique_ptr<CallbackThread> thread_calling_shutdown_; |
| 188 std::unique_ptr<CallbackThread> thread_calling_flush_for_testing_; | |
| 168 | 189 |
| 169 // Synchronizes accesses to |num_tasks_executed_|. | 190 // Synchronizes accesses to |num_tasks_executed_|. |
| 170 SchedulerLock lock_; | 191 SchedulerLock lock_; |
| 171 | 192 |
| 172 size_t num_tasks_executed_ = 0; | 193 size_t num_tasks_executed_ = 0; |
| 173 | 194 |
| 174 DISALLOW_COPY_AND_ASSIGN(TaskSchedulerTaskTrackerTest); | 195 DISALLOW_COPY_AND_ASSIGN(TaskSchedulerTaskTrackerTest); |
| 175 }; | 196 }; |
| 176 | 197 |
| 177 #define WAIT_FOR_ASYNC_SHUTDOWN_COMPLETED() \ | 198 #define WAIT_FOR_ASYNC_SHUTDOWN_COMPLETED() \ |
| 178 do { \ | 199 do { \ |
| 179 SCOPED_TRACE(""); \ | 200 SCOPED_TRACE(""); \ |
| 180 WaitForAsyncIsShutdownComplete(); \ | 201 WaitForAsyncIsShutdownComplete(); \ |
| 181 } while (false) | 202 } while (false) |
| 182 | 203 |
| 183 #define VERIFY_ASYNC_SHUTDOWN_IN_PROGRESS() \ | 204 #define VERIFY_ASYNC_SHUTDOWN_IN_PROGRESS() \ |
| 184 do { \ | 205 do { \ |
| 185 SCOPED_TRACE(""); \ | 206 SCOPED_TRACE(""); \ |
| 186 VerifyAsyncShutdownInProgress(); \ | 207 VerifyAsyncShutdownInProgress(); \ |
| 187 } while (false) | 208 } while (false) |
| 188 | 209 |
| 210 #define WAIT_FOR_ASYNC_FLUSH_FOR_TESTING_RETURNED() \ | |
| 211 do { \ | |
| 212 SCOPED_TRACE(""); \ | |
| 213 WaitForAsyncFlushForTestingReturned(); \ | |
| 214 } while (false) | |
| 215 | |
| 216 #define VERIFY_ASYNC_FLUSH_FOR_TESTING_IN_PROGRESS() \ | |
| 217 do { \ | |
| 218 SCOPED_TRACE(""); \ | |
| 219 VerifyAsyncFlushForTestingInProgress(); \ | |
| 220 } while (false) | |
| 221 | |
| 189 } // namespace | 222 } // namespace |
| 190 | 223 |
| 191 TEST_P(TaskSchedulerTaskTrackerTest, WillPostAndRunBeforeShutdown) { | 224 TEST_P(TaskSchedulerTaskTrackerTest, WillPostAndRunBeforeShutdown) { |
| 192 std::unique_ptr<Task> task(CreateTask(GetParam())); | 225 std::unique_ptr<Task> task(CreateTask(GetParam())); |
| 193 | 226 |
| 194 // Inform |task_tracker_| that |task| will be posted. | 227 // Inform |task_tracker_| that |task| will be posted. |
| 195 EXPECT_TRUE(tracker_.WillPostTask(task.get())); | 228 EXPECT_TRUE(tracker_.WillPostTask(task.get())); |
| 196 | 229 |
| 197 // Run the task. | 230 // Run the task. |
| 198 EXPECT_EQ(0U, NumTasksExecuted()); | 231 EXPECT_EQ(0U, NumTasksExecuted()); |
| (...skipping 249 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 448 // being set on it. | 481 // being set on it. |
| 449 std::unique_ptr<Task> verify_task( | 482 std::unique_ptr<Task> verify_task( |
| 450 new Task(FROM_HERE, Bind(&VerifyThreadTaskRunnerHandle, | 483 new Task(FROM_HERE, Bind(&VerifyThreadTaskRunnerHandle, |
| 451 base::Unretained(test_task_runner.get())), | 484 base::Unretained(test_task_runner.get())), |
| 452 TaskTraits().WithShutdownBehavior(GetParam()), TimeDelta())); | 485 TaskTraits().WithShutdownBehavior(GetParam()), TimeDelta())); |
| 453 verify_task->single_thread_task_runner_ref = test_task_runner; | 486 verify_task->single_thread_task_runner_ref = test_task_runner; |
| 454 | 487 |
| 455 RunTaskRunnerHandleVerificationTask(&tracker_, std::move(verify_task)); | 488 RunTaskRunnerHandleVerificationTask(&tracker_, std::move(verify_task)); |
| 456 } | 489 } |
| 457 | 490 |
| 491 TEST_P(TaskSchedulerTaskTrackerTest, FlushForTestingDelayedTask) { | |
| 492 const Task delayed_task(FROM_HERE, Bind(&DoNothing), | |
| 493 TaskTraits().WithShutdownBehavior(GetParam()), | |
| 494 TimeDelta::FromSeconds(10)); | |
|
gab
2016/09/23 15:39:18
Make this minutes so that the test would timeout i
fdoray
2016/09/23 15:56:50
Ok. But since I'm just testing TaskTracker (not th
gab
2016/09/23 17:05:25
Sure, but that makes it clear. Increasing readabil
| |
| 495 tracker_.WillPostTask(&delayed_task); | |
| 496 // FlushForTesting() should return even if the delayed task didn't run. | |
| 497 tracker_.FlushForTesting(); | |
| 498 } | |
| 499 | |
| 500 TEST_P(TaskSchedulerTaskTrackerTest, FlushForTestingUndelayedTask) { | |
| 501 const Task undelayed_task(FROM_HERE, Bind(&DoNothing), | |
| 502 TaskTraits().WithShutdownBehavior(GetParam()), | |
| 503 TimeDelta()); | |
| 504 tracker_.WillPostTask(&undelayed_task); | |
| 505 | |
| 506 // FlushForTesting() shouldn't return before the undelayed task runs. | |
| 507 CallFlushForTestingAsync(); | |
| 508 PlatformThread::Sleep(TestTimeouts::tiny_timeout()); | |
| 509 VERIFY_ASYNC_FLUSH_FOR_TESTING_IN_PROGRESS(); | |
| 510 | |
| 511 // FlushForTesting() should return after the undelayed task runs. | |
| 512 tracker_.RunTask(&undelayed_task, SequenceToken::Create()); | |
| 513 WAIT_FOR_ASYNC_FLUSH_FOR_TESTING_RETURNED(); | |
| 514 } | |
| 515 | |
| 516 TEST_P(TaskSchedulerTaskTrackerTest, FlushForTestingPostDuringCall) { | |
| 517 const Task undelayed_task(FROM_HERE, Bind(&DoNothing), | |
| 518 TaskTraits().WithShutdownBehavior(GetParam()), | |
| 519 TimeDelta()); | |
| 520 tracker_.WillPostTask(&undelayed_task); | |
| 521 | |
| 522 // FlushForTesting() shouldn't return before the undelayed task runs. | |
| 523 CallFlushForTestingAsync(); | |
| 524 PlatformThread::Sleep(TestTimeouts::tiny_timeout()); | |
| 525 VERIFY_ASYNC_FLUSH_FOR_TESTING_IN_PROGRESS(); | |
| 526 | |
| 527 // Simulate posting another undelayed task. | |
| 528 const Task other_undelayed_task(FROM_HERE, Bind(&DoNothing), | |
| 529 TaskTraits().WithShutdownBehavior(GetParam()), | |
| 530 TimeDelta()); | |
| 531 tracker_.WillPostTask(&other_undelayed_task); | |
| 532 | |
| 533 // Run the first undelayed task. | |
| 534 tracker_.RunTask(&undelayed_task, SequenceToken::Create()); | |
| 535 | |
| 536 // FlushForTesting() shouldn't return before the second undelayed task runs. | |
| 537 PlatformThread::Sleep(TestTimeouts::tiny_timeout()); | |
| 538 VERIFY_ASYNC_FLUSH_FOR_TESTING_IN_PROGRESS(); | |
| 539 | |
| 540 // FlushForTesting() should return after the second undelayed task runs. | |
| 541 tracker_.RunTask(&other_undelayed_task, SequenceToken::Create()); | |
| 542 WAIT_FOR_ASYNC_FLUSH_FOR_TESTING_RETURNED(); | |
| 543 } | |
| 544 | |
| 545 TEST_P(TaskSchedulerTaskTrackerTest, FlushForTestingAfterShutdown) { | |
| 546 if (GetParam() == TaskShutdownBehavior::BLOCK_SHUTDOWN) | |
| 547 return; | |
| 548 | |
| 549 // Simulate posting a task. | |
| 550 const Task undelayed_task(FROM_HERE, Bind(&DoNothing), | |
| 551 TaskTraits().WithShutdownBehavior(GetParam()), | |
| 552 TimeDelta()); | |
| 553 tracker_.WillPostTask(&undelayed_task); | |
| 554 | |
| 555 // Shutdown() should return immediately since there are no pending | |
| 556 // BLOCK_SHUTDOWN tasks. | |
| 557 tracker_.Shutdown(); | |
| 558 | |
| 559 // FlushForTesting() should return immediately after shutdown, even if an | |
| 560 // undelayed task hasn't run. | |
| 561 tracker_.FlushForTesting(); | |
| 562 } | |
| 563 | |
| 564 TEST_P(TaskSchedulerTaskTrackerTest, ShutdownDuringFlushForTesting) { | |
| 565 if (GetParam() == TaskShutdownBehavior::BLOCK_SHUTDOWN) | |
| 566 return; | |
| 567 | |
| 568 // Simulate posting a task. | |
| 569 const Task undelayed_task(FROM_HERE, Bind(&DoNothing), | |
| 570 TaskTraits().WithShutdownBehavior(GetParam()), | |
| 571 TimeDelta()); | |
| 572 tracker_.WillPostTask(&undelayed_task); | |
| 573 | |
| 574 // FlushForTesting() shouldn't return before the undelayed task runs or | |
| 575 // shutdown completes. | |
| 576 CallFlushForTestingAsync(); | |
| 577 PlatformThread::Sleep(TestTimeouts::tiny_timeout()); | |
| 578 VERIFY_ASYNC_FLUSH_FOR_TESTING_IN_PROGRESS(); | |
| 579 | |
| 580 // Shutdown() should return immediately since there are no pending | |
| 581 // BLOCK_SHUTDOWN tasks. | |
| 582 tracker_.Shutdown(); | |
| 583 | |
| 584 // FlushForTesting() should now return, even if an undelayed task hasn't run. | |
| 585 WAIT_FOR_ASYNC_FLUSH_FOR_TESTING_RETURNED(); | |
| 586 } | |
| 587 | |
| 458 INSTANTIATE_TEST_CASE_P( | 588 INSTANTIATE_TEST_CASE_P( |
| 459 ContinueOnShutdown, | 589 ContinueOnShutdown, |
| 460 TaskSchedulerTaskTrackerTest, | 590 TaskSchedulerTaskTrackerTest, |
| 461 ::testing::Values(TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN)); | 591 ::testing::Values(TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN)); |
| 462 INSTANTIATE_TEST_CASE_P( | 592 INSTANTIATE_TEST_CASE_P( |
| 463 SkipOnShutdown, | 593 SkipOnShutdown, |
| 464 TaskSchedulerTaskTrackerTest, | 594 TaskSchedulerTaskTrackerTest, |
| 465 ::testing::Values(TaskShutdownBehavior::SKIP_ON_SHUTDOWN)); | 595 ::testing::Values(TaskShutdownBehavior::SKIP_ON_SHUTDOWN)); |
| 466 INSTANTIATE_TEST_CASE_P( | 596 INSTANTIATE_TEST_CASE_P( |
| 467 BlockShutdown, | 597 BlockShutdown, |
| (...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 620 | 750 |
| 621 // Unblock shutdown by running |block_shutdown_task|. | 751 // Unblock shutdown by running |block_shutdown_task|. |
| 622 EXPECT_TRUE( | 752 EXPECT_TRUE( |
| 623 tracker_.RunTask(block_shutdown_task.get(), SequenceToken::Create())); | 753 tracker_.RunTask(block_shutdown_task.get(), SequenceToken::Create())); |
| 624 EXPECT_EQ(kLoadTestNumIterations + 1, NumTasksExecuted()); | 754 EXPECT_EQ(kLoadTestNumIterations + 1, NumTasksExecuted()); |
| 625 WAIT_FOR_ASYNC_SHUTDOWN_COMPLETED(); | 755 WAIT_FOR_ASYNC_SHUTDOWN_COMPLETED(); |
| 626 } | 756 } |
| 627 | 757 |
| 628 } // namespace internal | 758 } // namespace internal |
| 629 } // namespace base | 759 } // namespace base |
| OLD | NEW |