Chromium Code Reviews| Index: base/task_scheduler/scheduler_worker_pool_impl_unittest.cc |
| diff --git a/base/task_scheduler/scheduler_worker_pool_impl_unittest.cc b/base/task_scheduler/scheduler_worker_pool_impl_unittest.cc |
| index 9c96883058e4c5481ab4091cd81ac8dd1cf0bcaa..af694a9ce0b4a16d894a719cb93611060f53248b 100644 |
| --- a/base/task_scheduler/scheduler_worker_pool_impl_unittest.cc |
| +++ b/base/task_scheduler/scheduler_worker_pool_impl_unittest.cc |
| @@ -10,6 +10,7 @@ |
| #include <unordered_set> |
| #include <vector> |
| +#include "base/atomicops.h" |
| #include "base/bind.h" |
| #include "base/bind_helpers.h" |
| #include "base/callback.h" |
| @@ -29,7 +30,10 @@ |
| #include "base/task_scheduler/test_utils.h" |
| #include "base/threading/platform_thread.h" |
| #include "base/threading/simple_thread.h" |
| +#include "base/threading/thread_checker_impl.h" |
| +#include "base/threading/thread_local_storage.h" |
| #include "base/threading/thread_restrictions.h" |
| +#include "base/time/time.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| namespace base { |
| @@ -39,6 +43,7 @@ namespace { |
| const size_t kNumWorkersInWorkerPool = 4; |
| const size_t kNumThreadsPostingTasks = 4; |
| const size_t kNumTasksPostedPerThread = 150; |
| +const TimeDelta kSuggestedReclaimTime = TimeDelta::FromMilliseconds(10); |
|
gab
2016/07/20 01:45:21
"kSuggestedReclaimTimeForDetachTests" or something
gab
2016/07/20 01:45:21
constexpr
robliao
2016/07/20 19:44:01
Done.
|
| using IORestriction = SchedulerWorkerPoolParams::IORestriction; |
| @@ -63,22 +68,28 @@ class TaskSchedulerWorkerPoolImplTest |
| TaskSchedulerWorkerPoolImplTest() = default; |
| void SetUp() override { |
| + InitializeWorkerPool(TimeDelta::Max()); |
| + worker_pool_->DisallowWorkerDetachmentForTesting(); |
|
fdoray
2016/07/20 14:15:43
This is not required given that suggested reclaim
robliao
2016/07/20 19:44:01
Yep. Done.
|
| + } |
| + |
| + void TearDown() override { |
| + worker_pool_->WaitForAllWorkersIdleForTesting(); |
| + worker_pool_->JoinForTesting(); |
| + } |
| + |
| + void InitializeWorkerPool(const TimeDelta& suggested_reclaim_time) { |
| worker_pool_ = SchedulerWorkerPoolImpl::Create( |
| SchedulerWorkerPoolParams("TestWorkerPoolWithFileIO", |
| ThreadPriority::NORMAL, |
| IORestriction::ALLOWED, |
| - kNumWorkersInWorkerPool), |
| + kNumWorkersInWorkerPool, |
| + suggested_reclaim_time), |
| Bind(&TaskSchedulerWorkerPoolImplTest::ReEnqueueSequenceCallback, |
| Unretained(this)), |
| &task_tracker_, &delayed_task_manager_); |
| ASSERT_TRUE(worker_pool_); |
| } |
| - void TearDown() override { |
| - worker_pool_->WaitForAllWorkersIdleForTesting(); |
| - worker_pool_->JoinForTesting(); |
| - } |
| - |
| std::unique_ptr<SchedulerWorkerPoolImpl> worker_pool_; |
| TaskTracker task_tracker_; |
| @@ -367,10 +378,12 @@ TEST_P(TaskSchedulerWorkerPoolImplIORestrictionTest, IORestriction) { |
| auto worker_pool = SchedulerWorkerPoolImpl::Create( |
| SchedulerWorkerPoolParams("TestWorkerPoolWithParam", |
| - ThreadPriority::NORMAL, GetParam(), 1U), |
| + ThreadPriority::NORMAL, GetParam(), 1U, |
| + TimeDelta::Max()), |
| Bind(&NotReachedReEnqueueSequenceCallback), &task_tracker, |
| &delayed_task_manager); |
| ASSERT_TRUE(worker_pool); |
| + worker_pool->DisallowWorkerDetachmentForTesting(); |
|
fdoray
2016/07/20 14:15:43
This is not required given that suggested reclaim
robliao
2016/07/20 19:44:01
Yep. Done.
|
| WaitableEvent task_ran(WaitableEvent::ResetPolicy::MANUAL, |
| WaitableEvent::InitialState::NOT_SIGNALED); |
| @@ -388,5 +401,161 @@ INSTANTIATE_TEST_CASE_P(IODisallowed, |
| TaskSchedulerWorkerPoolImplIORestrictionTest, |
| ::testing::Values(IORestriction::DISALLOWED)); |
| +namespace { |
| + |
| +class TaskSchedulerWorkerPoolSingleThreadedTest |
| + : public TaskSchedulerWorkerPoolImplTest { |
| + public: |
| + void InitializeThreadChecker() { |
| + thread_checker_.reset(new ThreadCheckerImpl()); |
| + } |
| + |
| + void CheckValidThread() { |
| + EXPECT_TRUE(thread_checker_->CalledOnValidThread()); |
| + } |
| + |
| + protected: |
| + void SetUp() override { |
| + InitializeWorkerPool(kSuggestedReclaimTime); |
| + } |
| + |
| + TaskSchedulerWorkerPoolSingleThreadedTest() = default; |
| + |
| + private: |
| + std::unique_ptr<ThreadCheckerImpl> thread_checker_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(TaskSchedulerWorkerPoolSingleThreadedTest); |
| +}; |
| + |
| +} // namespace |
| + |
| +// Verify that thread resources for a single thread remain. |
| +TEST_F(TaskSchedulerWorkerPoolSingleThreadedTest, SingleThreadTask) { |
| + auto single_thread_task_runner = |
| + worker_pool_->CreateTaskRunnerWithTraits( |
| + TaskTraits(). |
| + WithShutdownBehavior(TaskShutdownBehavior::BLOCK_SHUTDOWN), |
| + ExecutionMode::SINGLE_THREADED); |
| + single_thread_task_runner->PostTask( |
| + FROM_HERE, |
| + Bind(&TaskSchedulerWorkerPoolSingleThreadedTest::InitializeThreadChecker, |
| + Unretained(this))); |
| + WaitableEvent task_waiter(WaitableEvent::ResetPolicy::AUTOMATIC, |
| + WaitableEvent::InitialState::NOT_SIGNALED); |
| + single_thread_task_runner->PostTask( |
| + FROM_HERE, Bind(&WaitableEvent::Signal, Unretained(&task_waiter))); |
| + task_waiter.Wait(); |
| + worker_pool_->WaitForAllWorkersIdleForTesting(); |
| + |
| + // Give the worker pool a chance to reclaim its threads. |
| + PlatformThread::Sleep( |
| + kSuggestedReclaimTime + TimeDelta::FromMilliseconds(200)); |
| + |
| + worker_pool_->DisallowWorkerDetachmentForTesting(); |
| + |
| + single_thread_task_runner->PostTask( |
| + FROM_HERE, |
| + Bind(&TaskSchedulerWorkerPoolSingleThreadedTest::CheckValidThread, |
| + Unretained(this))); |
| + single_thread_task_runner->PostTask( |
| + FROM_HERE, Bind(&WaitableEvent::Signal, Unretained(&task_waiter))); |
| + task_waiter.Wait(); |
| +} |
| + |
| +namespace { |
| + |
| +constexpr size_t kMagicTlsValue = 42; |
| + |
| +class TaskSchedulerWorkerPoolCheckTlsReuse |
| + : public TaskSchedulerWorkerPoolImplTest { |
| + public: |
| + void SetTlsValueAndWait() { |
| + slot_.Set(reinterpret_cast<void*>(kMagicTlsValue)); |
| + waiter_.Wait(); |
| + } |
| + |
| + void CountZeroTlsValuesAndWait(WaitableEvent* count_waiter) { |
|
gab
2016/07/20 01:45:21
s/ZeroTlsValue/NoMagicTlsValue/ (here and below fo
robliao
2016/07/20 19:44:01
We're counting zero TLS values here so the name sh
|
| + if (!slot_.Get()) |
| + subtle::NoBarrier_AtomicIncrement(&zero_tls_values_, 1); |
| + |
| + count_waiter->Signal(); |
| + waiter_.Wait(); |
| + } |
| + |
| + protected: |
| + TaskSchedulerWorkerPoolCheckTlsReuse() : |
| + waiter_(WaitableEvent::ResetPolicy::MANUAL, |
| + WaitableEvent::InitialState::NOT_SIGNALED) {} |
| + |
| + void SetUp() override { |
| + InitializeWorkerPool(kSuggestedReclaimTime); |
| + } |
| + |
| + subtle::Atomic32 zero_tls_values_ = 0; |
| + |
| + WaitableEvent waiter_; |
| + |
| + private: |
| + ThreadLocalStorage::Slot slot_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(TaskSchedulerWorkerPoolCheckTlsReuse); |
| +}; |
| + |
| +} // namespace |
| + |
| +// Checks that at least one thread has detached by checking the TLS. |
| +TEST_F(TaskSchedulerWorkerPoolCheckTlsReuse, CheckDetachedThreads) { |
| + // Saturate the threads and mark each thread with a magic TLS value. |
| + std::vector<std::unique_ptr<test::TestTaskFactory>> factories; |
| + for (size_t i = 0; i < kNumWorkersInWorkerPool; ++i) { |
| + factories.push_back(WrapUnique(new test::TestTaskFactory( |
| + worker_pool_->CreateTaskRunnerWithTraits( |
| + TaskTraits(), ExecutionMode::PARALLEL), |
| + ExecutionMode::PARALLEL))); |
| + ASSERT_TRUE(factories.back()->PostTask( |
| + PostNestedTask::NO, |
| + Bind(&TaskSchedulerWorkerPoolCheckTlsReuse::SetTlsValueAndWait, |
| + Unretained(this)))); |
| + factories.back()->WaitForAllTasksToRun(); |
| + } |
| + |
| + // Release tasks waiting on |waiter_|. |
| + waiter_.Signal(); |
| + worker_pool_->WaitForAllWorkersIdleForTesting(); |
| + |
| + // All threads should be done running by now, so reset for the next phase. |
| + waiter_.Reset(); |
| + |
| + // Give the worker pool a chance to detach its threads. |
| + PlatformThread::Sleep( |
| + kSuggestedReclaimTime + TimeDelta::FromMilliseconds(200)); |
| + |
| + worker_pool_->DisallowWorkerDetachmentForTesting(); |
| + |
| + // Saturate and count the threads that do not have the magic TLS value. If the |
| + // value is not there, that means we're at a new thread. |
| + std::vector<std::unique_ptr<WaitableEvent>> count_waiters; |
| + for (auto& factory : factories) { |
| + count_waiters.push_back(WrapUnique(new WaitableEvent( |
| + WaitableEvent::ResetPolicy::MANUAL, |
| + WaitableEvent::InitialState::NOT_SIGNALED))); |
| + ASSERT_TRUE(factory->PostTask( |
| + PostNestedTask::NO, |
| + Bind(&TaskSchedulerWorkerPoolCheckTlsReuse::CountZeroTlsValuesAndWait, |
| + Unretained(this), |
| + count_waiters.back().get()))); |
| + factory->WaitForAllTasksToRun(); |
| + } |
| + |
| + // Wait for all counters to complete. |
| + for (auto& count_waiter : count_waiters) |
| + count_waiter->Wait(); |
| + |
| + EXPECT_GT(zero_tls_values_, 0); |
|
gab
2016/07/20 01:45:21
Should this use NoBarrier_Load()? On Windows this
fdoray
2016/07/20 14:15:43
atomicops.h:19 It is incorrect to make direct ass
robliao
2016/07/20 19:44:01
The atomicops unittest direct references their var
|
| + |
| + // Release tasks waiting on |waiter_|. |
| + waiter_.Signal(); |
| +} |
| + |
| } // namespace internal |
| } // namespace base |