Index: base/task_scheduler/scheduler_worker_unittest.cc~RF3e0a7fad.TMP |
diff --git a/base/task_scheduler/scheduler_worker_unittest.cc~RF3e0a7fad.TMP b/base/task_scheduler/scheduler_worker_unittest.cc~RF3e0a7fad.TMP |
deleted file mode 100644 |
index 1ab3d45fee8532190127182e89fe86a1bd90a38a..0000000000000000000000000000000000000000 |
--- a/base/task_scheduler/scheduler_worker_unittest.cc~RF3e0a7fad.TMP |
+++ /dev/null |
@@ -1,552 +0,0 @@ |
-// Copyright 2016 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-#include "base/task_scheduler/scheduler_worker.h" |
- |
-#include <stddef.h> |
- |
-#include <memory> |
-#include <vector> |
- |
-#include "base/bind.h" |
-#include "base/bind_helpers.h" |
-#include "base/macros.h" |
-#include "base/memory/ptr_util.h" |
-#include "base/synchronization/condition_variable.h" |
-#include "base/synchronization/waitable_event.h" |
-#include "base/task_scheduler/scheduler_lock.h" |
-#include "base/task_scheduler/sequence.h" |
-#include "base/task_scheduler/task.h" |
-#include "base/task_scheduler/task_tracker.h" |
-#include "base/threading/platform_thread.h" |
-#include "base/time/time.h" |
-#include "build/build_config.h" |
-#include "testing/gmock/include/gmock/gmock.h" |
-#include "testing/gtest/include/gtest/gtest.h" |
- |
-using testing::_; |
-using testing::Mock; |
-using testing::Ne; |
-using testing::StrictMock; |
- |
-namespace base { |
-namespace internal { |
-namespace { |
- |
-const size_t kNumSequencesPerTest = 150; |
- |
-class SchedulerWorkerDefaultDelegate : public SchedulerWorker::Delegate { |
- public: |
- SchedulerWorkerDefaultDelegate() = default; |
- |
- // SchedulerWorker::Delegate: |
- void OnMainEntry(SchedulerWorker* worker, const TimeDelta& detach_duration) override {} |
- scoped_refptr<Sequence> GetWork(SchedulerWorker* worker) override { return nullptr; } |
- void ReEnqueueSequence(scoped_refptr<Sequence> sequence) override { |
- ADD_FAILURE() << "Unexpected call to ReEnqueueSequence()"; |
- } |
- TimeDelta GetSleepTimeout() override { return TimeDelta::Max(); } |
- bool CanDetach(SchedulerWorker* worker) override { return false; } |
- |
- private: |
- DISALLOW_COPY_AND_ASSIGN(SchedulerWorkerDefaultDelegate); |
-}; |
- |
-// The test parameter is the number of Tasks per Sequence returned by GetWork(). |
-class TaskSchedulerWorkerTest : public testing::TestWithParam<size_t> { |
- protected: |
- TaskSchedulerWorkerTest() |
- : main_entry_called_(WaitableEvent::ResetPolicy::MANUAL, |
- WaitableEvent::InitialState::NOT_SIGNALED), |
- num_get_work_cv_(lock_.CreateConditionVariable()), |
- worker_set_(WaitableEvent::ResetPolicy::MANUAL, |
- WaitableEvent::InitialState::NOT_SIGNALED) {} |
- |
- void SetUp() override { |
- worker_ = SchedulerWorker::Create( |
- ThreadPriority::NORMAL, |
- WrapUnique(new TestSchedulerWorkerDelegate(this)), |
- &task_tracker_, |
- SchedulerWorker::InitialState::ALIVE); |
- ASSERT_TRUE(worker_); |
- worker_set_.Signal(); |
- main_entry_called_.Wait(); |
- } |
- |
- void TearDown() override { |
- worker_->JoinForTesting(); |
- } |
- |
- size_t TasksPerSequence() const { return GetParam(); } |
- |
- // Wait until GetWork() has been called |num_get_work| times. |
- void WaitForNumGetWork(size_t num_get_work) { |
- AutoSchedulerLock auto_lock(lock_); |
- while (num_get_work_ < num_get_work) |
- num_get_work_cv_->Wait(); |
- } |
- |
- void SetMaxGetWork(size_t max_get_work) { |
- AutoSchedulerLock auto_lock(lock_); |
- max_get_work_ = max_get_work; |
- } |
- |
- void SetNumSequencesToCreate(size_t num_sequences_to_create) { |
- AutoSchedulerLock auto_lock(lock_); |
- EXPECT_EQ(0U, num_sequences_to_create_); |
- num_sequences_to_create_ = num_sequences_to_create; |
- } |
- |
- size_t NumRunTasks() { |
- AutoSchedulerLock auto_lock(lock_); |
- return num_run_tasks_; |
- } |
- |
- std::vector<scoped_refptr<Sequence>> CreatedSequences() { |
- AutoSchedulerLock auto_lock(lock_); |
- return created_sequences_; |
- } |
- |
- std::vector<scoped_refptr<Sequence>> EnqueuedSequences() { |
- AutoSchedulerLock auto_lock(lock_); |
- return re_enqueued_sequences_; |
- } |
- |
- std::unique_ptr<SchedulerWorker> worker_; |
- |
- private: |
- class TestSchedulerWorkerDelegate : public SchedulerWorkerDefaultDelegate { |
- public: |
- TestSchedulerWorkerDelegate(TaskSchedulerWorkerTest* outer) |
- : outer_(outer) {} |
- |
- // SchedulerWorker::Delegate: |
- void OnMainEntry(SchedulerWorker* worker, |
- const TimeDelta& detach_duration) override { |
- outer_->worker_set_.Wait(); |
- EXPECT_EQ(outer_->worker_.get(), worker); |
- |
- // Without synchronization, OnMainEntry() could be called twice without |
- // generating an error. |
- AutoSchedulerLock auto_lock(outer_->lock_); |
- EXPECT_FALSE(outer_->main_entry_called_.IsSignaled()); |
- outer_->main_entry_called_.Signal(); |
- } |
- |
- scoped_refptr<Sequence> GetWork(SchedulerWorker* worker) override { |
- EXPECT_EQ(outer_->worker_.get(), worker); |
- |
- { |
- AutoSchedulerLock auto_lock(outer_->lock_); |
- |
- // Increment the number of times that this method has been called. |
- ++outer_->num_get_work_; |
- outer_->num_get_work_cv_->Signal(); |
- |
- // Verify that this method isn't called more times than expected. |
- EXPECT_LE(outer_->num_get_work_, outer_->max_get_work_); |
- |
- // Check if a Sequence should be returned. |
- if (outer_->num_sequences_to_create_ == 0) |
- return nullptr; |
- --outer_->num_sequences_to_create_; |
- } |
- |
- // Create a Sequence with TasksPerSequence() Tasks. |
- scoped_refptr<Sequence> sequence(new Sequence); |
- for (size_t i = 0; i < outer_->TasksPerSequence(); ++i) { |
- std::unique_ptr<Task> task(new Task( |
- FROM_HERE, Bind(&TaskSchedulerWorkerTest::RunTaskCallback, |
- Unretained(outer_)), |
- TaskTraits(), TimeDelta())); |
- EXPECT_TRUE(outer_->task_tracker_.WillPostTask(task.get())); |
- sequence->PushTask(std::move(task)); |
- } |
- |
- { |
- // Add the Sequence to the vector of created Sequences. |
- AutoSchedulerLock auto_lock(outer_->lock_); |
- outer_->created_sequences_.push_back(sequence); |
- } |
- |
- return sequence; |
- } |
- |
- // This override verifies that |sequence| contains the expected number of |
- // Tasks and adds it to |enqueued_sequences_|. Unlike a normal |
- // EnqueueSequence implementation, it doesn't reinsert |sequence| into a |
- // queue for further execution. |
- void ReEnqueueSequence(scoped_refptr<Sequence> sequence) override { |
- EXPECT_GT(outer_->TasksPerSequence(), 1U); |
- |
- // Verify that |sequence| contains TasksPerSequence() - 1 Tasks. |
- for (size_t i = 0; i < outer_->TasksPerSequence() - 1; ++i) { |
- EXPECT_TRUE(sequence->PeekTask()); |
- sequence->PopTask(); |
- } |
- EXPECT_FALSE(sequence->PeekTask()); |
- |
- // Add |sequence| to |re_enqueued_sequences_|. |
- AutoSchedulerLock auto_lock(outer_->lock_); |
- outer_->re_enqueued_sequences_.push_back(std::move(sequence)); |
- EXPECT_LE(outer_->re_enqueued_sequences_.size(), |
- outer_->created_sequences_.size()); |
- } |
- |
- private: |
- TaskSchedulerWorkerTest* outer_; |
- }; |
- |
- void RunTaskCallback() { |
- AutoSchedulerLock auto_lock(lock_); |
- ++num_run_tasks_; |
- EXPECT_LE(num_run_tasks_, created_sequences_.size()); |
- } |
- |
- TaskTracker task_tracker_; |
- |
- // Synchronizes access to all members below. |
- mutable SchedulerLock lock_; |
- |
- // Signaled once OnMainEntry() has been called. |
- WaitableEvent main_entry_called_; |
- |
- // Number of Sequences that should be created by GetWork(). When this |
- // is 0, GetWork() returns nullptr. |
- size_t num_sequences_to_create_ = 0; |
- |
- // Number of times that GetWork() has been called. |
- size_t num_get_work_ = 0; |
- |
- // Maximum number of times that GetWork() can be called. |
- size_t max_get_work_ = 0; |
- |
- // Condition variable signaled when |num_get_work_| is incremented. |
- std::unique_ptr<ConditionVariable> num_get_work_cv_; |
- |
- // Sequences created by GetWork(). |
- std::vector<scoped_refptr<Sequence>> created_sequences_; |
- |
- // Sequences passed to EnqueueSequence(). |
- std::vector<scoped_refptr<Sequence>> re_enqueued_sequences_; |
- |
- // Number of times that RunTaskCallback() has been called. |
- size_t num_run_tasks_ = 0; |
- |
- // Signaled after |worker_| is set. |
- WaitableEvent worker_set_; |
- |
- DISALLOW_COPY_AND_ASSIGN(TaskSchedulerWorkerTest); |
-}; |
- |
-// Verify that when GetWork() continuously returns Sequences, all Tasks in these |
-// Sequences run successfully. The test wakes up the SchedulerWorker once. |
-TEST_P(TaskSchedulerWorkerTest, ContinuousWork) { |
- // Set GetWork() to return |kNumSequencesPerTest| Sequences before starting to |
- // return nullptr. |
- SetNumSequencesToCreate(kNumSequencesPerTest); |
- |
- // Expect |kNumSequencesPerTest| calls to GetWork() in which it returns a |
- // Sequence and one call in which its returns nullptr. |
- const size_t kExpectedNumGetWork = kNumSequencesPerTest + 1; |
- SetMaxGetWork(kExpectedNumGetWork); |
- |
- // Wake up |worker_| and wait until GetWork() has been invoked the |
- // expected amount of times. |
- worker_->WakeUp(); |
- WaitForNumGetWork(kExpectedNumGetWork); |
- |
- // All tasks should have run. |
- EXPECT_EQ(kNumSequencesPerTest, NumRunTasks()); |
- |
- // If Sequences returned by GetWork() contain more than one Task, they aren't |
- // empty after the worker pops Tasks from them and thus should be returned to |
- // EnqueueSequence(). |
- if (TasksPerSequence() > 1) |
- EXPECT_EQ(CreatedSequences(), EnqueuedSequences()); |
- else |
- EXPECT_TRUE(EnqueuedSequences().empty()); |
-} |
- |
-// Verify that when GetWork() alternates between returning a Sequence and |
-// returning nullptr, all Tasks in the returned Sequences run successfully. The |
-// test wakes up the SchedulerWorker once for each Sequence. |
-TEST_P(TaskSchedulerWorkerTest, IntermittentWork) { |
- for (size_t i = 0; i < kNumSequencesPerTest; ++i) { |
- // Set GetWork() to return 1 Sequence before starting to return |
- // nullptr. |
- SetNumSequencesToCreate(1); |
- |
- // Expect |i + 1| calls to GetWork() in which it returns a Sequence and |
- // |i + 1| calls in which it returns nullptr. |
- const size_t expected_num_get_work = 2 * (i + 1); |
- SetMaxGetWork(expected_num_get_work); |
- |
- // Wake up |worker_| and wait until GetWork() has been invoked |
- // the expected amount of times. |
- worker_->WakeUp(); |
- WaitForNumGetWork(expected_num_get_work); |
- |
- // The Task should have run |
- EXPECT_EQ(i + 1, NumRunTasks()); |
- |
- // If Sequences returned by GetWork() contain more than one Task, they |
- // aren't empty after the worker pops Tasks from them and thus should be |
- // returned to EnqueueSequence(). |
- if (TasksPerSequence() > 1) |
- EXPECT_EQ(CreatedSequences(), EnqueuedSequences()); |
- else |
- EXPECT_TRUE(EnqueuedSequences().empty()); |
- } |
-} |
- |
-INSTANTIATE_TEST_CASE_P(OneTaskPerSequence, |
- TaskSchedulerWorkerTest, |
- ::testing::Values(1)); |
-INSTANTIATE_TEST_CASE_P(TwoTasksPerSequence, |
- TaskSchedulerWorkerTest, |
- ::testing::Values(2)); |
- |
-namespace { |
- |
-class ControllableDetachDelegate : public SchedulerWorkerDefaultDelegate { |
- public: |
- ControllableDetachDelegate() |
- : work_processed_(WaitableEvent::ResetPolicy::MANUAL, |
- WaitableEvent::InitialState::NOT_SIGNALED), |
- detach_requested_(WaitableEvent::ResetPolicy::MANUAL, |
- WaitableEvent::InitialState::NOT_SIGNALED) {} |
- |
- ~ControllableDetachDelegate() override = default; |
- |
- // SchedulerWorker::Delegate: |
- MOCK_METHOD2(OnMainEntry, |
- void(SchedulerWorker* worker, const TimeDelta& detach_duration)); |
- |
- scoped_refptr<Sequence> GetWork(SchedulerWorker* worker) |
- override { |
- // Sends one item of work to signal |work_processed_|. On subsequent calls, |
- // sends nullptr to indicate there's no more work to be done. |
- if (work_requested_) |
- return nullptr; |
- |
- work_requested_ = true; |
- scoped_refptr<Sequence> sequence(new Sequence); |
- std::unique_ptr<Task> task(new Task( |
- FROM_HERE, Bind(&WaitableEvent::Signal, Unretained(&work_processed_)), |
- TaskTraits(), TimeDelta())); |
- sequence->PushTask(std::move(task)); |
- return sequence; |
- } |
- |
- bool CanDetach(SchedulerWorker* worker) override { |
- detach_requested_.Signal(); |
- return can_detach_; |
- } |
- |
- void WaitForWorkToRun() { |
- work_processed_.Wait(); |
- } |
- |
- void WaitForDetachRequest() { |
- detach_requested_.Wait(); |
- } |
- |
- void ResetState() { |
- work_requested_ = false; |
- work_processed_.Reset(); |
- detach_requested_.Reset(); |
- } |
- |
- void set_can_detach(bool can_detach) { can_detach_ = can_detach; } |
- |
- private: |
- bool work_requested_ = false; |
- bool can_detach_ = false; |
- WaitableEvent work_processed_; |
- WaitableEvent detach_requested_; |
- |
- DISALLOW_COPY_AND_ASSIGN(ControllableDetachDelegate); |
-}; |
- |
-} // namespace |
- |
-TEST(TaskSchedulerWorkerTest, WorkerDetaches) { |
- TaskTracker task_tracker; |
- // Will be owned by SchedulerWorker. |
- ControllableDetachDelegate* delegate = |
- new StrictMock<ControllableDetachDelegate>; |
- delegate->set_can_detach(true); |
- EXPECT_CALL(*delegate, OnMainEntry(_, TimeDelta::Max())); |
- std::unique_ptr<SchedulerWorker> worker = |
- SchedulerWorker::Create( |
- ThreadPriority::NORMAL, WrapUnique(delegate), &task_tracker, |
- SchedulerWorker::InitialState::ALIVE); |
- worker->WakeUp(); |
- delegate->WaitForWorkToRun(); |
- Mock::VerifyAndClear(delegate); |
- delegate->WaitForDetachRequest(); |
- // Sleep to give a chance for the detach to happen. A yield is too short. |
- PlatformThread::Sleep(TimeDelta::FromMilliseconds(50)); |
- ASSERT_FALSE(worker->ThreadAliveForTesting()); |
-} |
- |
-TEST(TaskSchedulerWorkerTest, WorkerDetachesAndWakes) { |
- TaskTracker task_tracker; |
- // Will be owned by SchedulerWorker. |
- ControllableDetachDelegate* delegate = |
- new StrictMock<ControllableDetachDelegate>; |
- delegate->set_can_detach(true); |
- EXPECT_CALL(*delegate, OnMainEntry(_, TimeDelta::Max())); |
- std::unique_ptr<SchedulerWorker> worker = |
- SchedulerWorker::Create( |
- ThreadPriority::NORMAL, WrapUnique(delegate), &task_tracker, |
- SchedulerWorker::InitialState::ALIVE); |
- worker->WakeUp(); |
- delegate->WaitForWorkToRun(); |
- Mock::VerifyAndClear(delegate); |
- delegate->WaitForDetachRequest(); |
- // Sleep to give a chance for the detach to happen. A yield is too short. |
- PlatformThread::Sleep(TimeDelta::FromMilliseconds(50)); |
- ASSERT_FALSE(worker->ThreadAliveForTesting()); |
- |
- delegate->ResetState(); |
- delegate->set_can_detach(false); |
- // When SchedulerWorker recreates its thread, expect OnMainEntry() to be |
- // called with a detach duration which is not TimeDelta::Max(). |
- EXPECT_CALL(*delegate, OnMainEntry(worker.get(), Ne(TimeDelta::Max()))); |
- worker->WakeUp(); |
- delegate->WaitForWorkToRun(); |
- Mock::VerifyAndClear(delegate); |
- delegate->WaitForDetachRequest(); |
- PlatformThread::Sleep(TimeDelta::FromMilliseconds(50)); |
- ASSERT_TRUE(worker->ThreadAliveForTesting()); |
- worker->JoinForTesting(); |
-} |
- |
-TEST(TaskSchedulerWorkerTest, CreateDetached) { |
- TaskTracker task_tracker; |
- // Will be owned by SchedulerWorker. |
- ControllableDetachDelegate* delegate = |
- new StrictMock<ControllableDetachDelegate>; |
- std::unique_ptr<SchedulerWorker> worker = |
- SchedulerWorker::Create( |
- ThreadPriority::NORMAL, WrapUnique(delegate), &task_tracker, |
- SchedulerWorker::InitialState::DETACHED); |
- ASSERT_FALSE(worker->ThreadAliveForTesting()); |
- EXPECT_CALL(*delegate, OnMainEntry(worker.get(), TimeDelta::Max())); |
- worker->WakeUp(); |
- delegate->WaitForWorkToRun(); |
- Mock::VerifyAndClear(delegate); |
- delegate->WaitForDetachRequest(); |
- ASSERT_TRUE(worker->ThreadAliveForTesting()); |
- worker->JoinForTesting(); |
-} |
- |
-namespace { |
- |
-class ExpectThreadPriorityDelegate : public SchedulerWorkerDefaultDelegate { |
- public: |
- ExpectThreadPriorityDelegate() |
- : priority_verified_in_get_work_event_( |
- WaitableEvent::ResetPolicy::AUTOMATIC, |
- WaitableEvent::InitialState::NOT_SIGNALED), |
- expected_thread_priority_(ThreadPriority::BACKGROUND) {} |
- |
- void SetExpectedThreadPriority(ThreadPriority expected_thread_priority) { |
- expected_thread_priority_ = expected_thread_priority; |
- } |
- |
- void WaitForPriorityVerifiedInGetWork() { |
- priority_verified_in_get_work_event_.Wait(); |
- } |
- |
- // SchedulerWorker::Delegate: |
- void OnMainEntry(SchedulerWorker* worker, |
- const TimeDelta& detach_duration) override { |
- VerifyThreadPriority(); |
- } |
- scoped_refptr<Sequence> GetWork(SchedulerWorker* worker) override { |
- VerifyThreadPriority(); |
- priority_verified_in_get_work_event_.Signal(); |
- return nullptr; |
- } |
- |
- private: |
- void VerifyThreadPriority() { |
- AutoSchedulerLock auto_lock(expected_thread_priority_lock_); |
- EXPECT_EQ(expected_thread_priority_, |
- PlatformThread::GetCurrentThreadPriority()); |
- } |
- |
- // Signaled after GetWork() has verified the priority of the worker thread. |
- WaitableEvent priority_verified_in_get_work_event_; |
- |
- // Synchronizes access to |expected_thread_priority_|. |
- SchedulerLock expected_thread_priority_lock_; |
- |
- // Expected thread priority for the next call to OnMainEntry() or GetWork(). |
- ThreadPriority expected_thread_priority_; |
- |
- DISALLOW_COPY_AND_ASSIGN(ExpectThreadPriorityDelegate); |
-}; |
- |
-} // namespace |
- |
-TEST(TaskSchedulerWorkerTest, BumpPriorityOfAliveThreadDuringShutdown) { |
- TaskTracker task_tracker; |
- |
- std::unique_ptr<ExpectThreadPriorityDelegate> delegate( |
- new ExpectThreadPriorityDelegate); |
- ExpectThreadPriorityDelegate* delegate_raw = delegate.get(); |
- delegate_raw->SetExpectedThreadPriority( |
- PlatformThread::CanIncreaseCurrentThreadPriority() |
- ? ThreadPriority::BACKGROUND |
- : ThreadPriority::NORMAL); |
- |
- std::unique_ptr<SchedulerWorker> worker = SchedulerWorker::Create( |
- ThreadPriority::BACKGROUND, std::move(delegate), &task_tracker, |
- SchedulerWorker::InitialState::ALIVE); |
- |
- // Verify that the initial thread priority is BACKGROUND (or NORMAL if thread |
- // priority can't be increased). |
- worker->WakeUp(); |
- delegate_raw->WaitForPriorityVerifiedInGetWork(); |
- |
- // Verify that the thread priority is bumped to NORMAL during shutdown. |
- delegate_raw->SetExpectedThreadPriority(ThreadPriority::NORMAL); |
- task_tracker.SetHasShutdownStartedForTesting(); |
- worker->WakeUp(); |
- delegate_raw->WaitForPriorityVerifiedInGetWork(); |
- |
- worker->JoinForTesting(); |
-} |
- |
-TEST(TaskSchedulerWorkerTest, BumpPriorityOfDetachedThreadDuringShutdown) { |
- TaskTracker task_tracker; |
- |
- std::unique_ptr<ExpectThreadPriorityDelegate> delegate( |
- new ExpectThreadPriorityDelegate); |
- ExpectThreadPriorityDelegate* delegate_raw = delegate.get(); |
- delegate_raw->SetExpectedThreadPriority(ThreadPriority::NORMAL); |
- |
- // Create a DETACHED thread. |
- std::unique_ptr<SchedulerWorker> worker = SchedulerWorker::Create( |
- ThreadPriority::BACKGROUND, std::move(delegate), &task_tracker, |
- SchedulerWorker::InitialState::DETACHED); |
- |
- // Pretend that shutdown has started. |
- task_tracker.SetHasShutdownStartedForTesting(); |
- |
- // Wake up the thread and verify that its priority is NORMAL when |
- // OnMainEntry() and GetWork() are called. |
- worker->WakeUp(); |
- delegate_raw->WaitForPriorityVerifiedInGetWork(); |
- |
- worker->JoinForTesting(); |
-} |
- |
-} // namespace |
-} // namespace internal |
-} // namespace base |