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 7766f72e0d2aecc16b2c085a1fae8c522d59ce18..572d885821c88a8c6bb801c195cd6ee5bf853aad 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" |
| @@ -28,7 +29,9 @@ |
| #include "base/task_scheduler/test_utils.h" |
| #include "base/threading/platform_thread.h" |
| #include "base/threading/simple_thread.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 { |
| @@ -38,6 +41,7 @@ namespace { |
| const size_t kNumWorkersInWorkerPool = 4; |
| const size_t kNumThreadsPostingTasks = 4; |
| const size_t kNumTasksPostedPerThread = 150; |
| +const TimeDelta kSuggestedReclaimTime = TimeDelta::FromMilliseconds(10); |
| using IORestriction = SchedulerWorkerPoolImpl::IORestriction; |
| @@ -65,6 +69,7 @@ class TaskSchedulerWorkerPoolImplTest |
| worker_pool_ = SchedulerWorkerPoolImpl::Create( |
| "TestWorkerPoolWithFileIO", ThreadPriority::NORMAL, |
| kNumWorkersInWorkerPool, IORestriction::ALLOWED, |
| + kSuggestedReclaimTime, |
| Bind(&TaskSchedulerWorkerPoolImplTest::ReEnqueueSequenceCallback, |
| Unretained(this)), |
| &task_tracker_, &delayed_task_manager_); |
| @@ -364,8 +369,8 @@ TEST_P(TaskSchedulerWorkerPoolImplIORestrictionTest, IORestriction) { |
| auto worker_pool = SchedulerWorkerPoolImpl::Create( |
| "TestWorkerPoolWithParam", ThreadPriority::NORMAL, 1U, GetParam(), |
| - Bind(&NotReachedReEnqueueSequenceCallback), &task_tracker, |
| - &delayed_task_manager); |
| + TimeDelta::Max(), Bind(&NotReachedReEnqueueSequenceCallback), |
| + &task_tracker, &delayed_task_manager); |
| ASSERT_TRUE(worker_pool); |
| WaitableEvent task_ran(WaitableEvent::ResetPolicy::MANUAL, |
| @@ -384,5 +389,152 @@ INSTANTIATE_TEST_CASE_P(IODisallowed, |
| TaskSchedulerWorkerPoolImplIORestrictionTest, |
| ::testing::Values(IORestriction::DISALLOWED)); |
| +namespace { |
| + |
| +constexpr size_t kMagicTlsValue = 42; |
| + |
| +class TaskSchedulerWorkerPoolSingleThreadedTest |
| + : public TaskSchedulerWorkerPoolImplTest { |
| + public: |
| + void SetTlsValue() { |
| + slot_.Set(reinterpret_cast<void*>(kMagicTlsValue)); |
| + } |
| + |
| + void CheckTlsValue() { |
| + EXPECT_EQ(kMagicTlsValue, reinterpret_cast<size_t>(slot_.Get())); |
| + } |
| + |
| + protected: |
| + TaskSchedulerWorkerPoolSingleThreadedTest() = default; |
| + |
| + private: |
| + ThreadLocalStorage::Slot slot_; |
| + |
| + 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::SetTlsValue, |
| + Unretained(this))); |
| + WaitableEvent task_waiter(WaitableEvent::ResetPolicy::MANUAL, |
| + WaitableEvent::InitialState::NOT_SIGNALED); |
| + single_thread_task_runner->PostTask( |
| + FROM_HERE, Bind(&WaitableEvent::Signal, Unretained(&task_waiter))); |
| + task_waiter.Wait(); |
| + task_waiter.Reset(); |
| + worker_pool_->WaitForAllWorkersIdleForTesting(); |
| + |
| + // Give the worker pool a chance to reclaim its threads. |
| + PlatformThread::Sleep( |
| + kSuggestedReclaimTime + TimeDelta::FromMilliseconds(200)); |
| + |
| + single_thread_task_runner->PostTask( |
| + FROM_HERE, |
| + Bind(&TaskSchedulerWorkerPoolSingleThreadedTest::CheckTlsValue, |
| + Unretained(this))); |
| + single_thread_task_runner->PostTask( |
| + FROM_HERE, Bind(&WaitableEvent::Signal, Unretained(&task_waiter))); |
| + task_waiter.Wait(); |
| +} |
| + |
|
fdoray
2016/07/04 19:41:33
Test that no detachment occurs when suggested recl
robliao
2016/07/07 17:49:16
If we could test this, this would be better handle
fdoray
2016/07/07 20:45:57
Acknowledged.
|
| +namespace { |
| + |
| +class TaskSchedulerWorkerPoolCheckTlsReuse |
| + : public TaskSchedulerWorkerPoolImplTest { |
| + public: |
| + void SetTlsValueAndWait() { |
| + slot_.Set(reinterpret_cast<void*>(kMagicTlsValue)); |
| + waiter_.Wait(); |
| + } |
| + |
| + void CountZeroTlsValues() { |
| + if (!slot_.Get()) |
| + subtle::NoBarrier_AtomicIncrement(&zero_tls_values_, 1); |
| + } |
| + |
| + protected: |
| + TaskSchedulerWorkerPoolCheckTlsReuse() : |
| + waiter_(WaitableEvent::ResetPolicy::MANUAL, |
| + WaitableEvent::InitialState::NOT_SIGNALED) {} |
| + 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(); |
|
fdoray
2016/07/04 19:41:33
Should the Reset() be after WaitForAllWorkersIdleF
robliao
2016/07/07 17:49:16
The tricky bit here is that we can't wait for all
fdoray
2016/07/07 20:45:57
If Signal() stays before WaitForAllWorkersIdleForT
robliao
2016/07/08 17:25:49
I initially misread this. Yup, let's move Reset af
|
| + waiter_.Reset(); |
| + |
| + worker_pool_->WaitForAllWorkersIdleForTesting(); |
| + |
| + // Give the worker pool a chance to detach its threads. |
| + PlatformThread::Sleep( |
| + kSuggestedReclaimTime + TimeDelta::FromMilliseconds(200)); |
| + |
| + // 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) { |
| + ASSERT_TRUE(factory->PostTask( |
| + PostNestedTask::NO, |
| + Bind(&TaskSchedulerWorkerPoolCheckTlsReuse::CountZeroTlsValues, |
|
fdoray
2016/07/04 19:41:32
Nothing prevents all CountZeroTlsValue tasks from
robliao
2016/07/07 17:49:16
Doesn't WaitForAllTasksToRun with a waiting task a
fdoray
2016/07/07 20:45:57
Line 486: You create factories with PARALLEL TaskR
robliao
2016/07/08 17:25:49
Ah yes, indeed. I've now moved both waitable event
|
| + Unretained(this)))); |
| + count_waiters.push_back(WrapUnique(new WaitableEvent( |
| + WaitableEvent::ResetPolicy::MANUAL, |
| + WaitableEvent::InitialState::NOT_SIGNALED))); |
| + ASSERT_TRUE(factory->PostTask( |
| + PostNestedTask::NO, |
| + Bind(&WaitableEvent::Signal, |
| + Unretained(count_waiters.back().get())))); |
| + ASSERT_TRUE(factory->PostTask(PostNestedTask::NO, |
| + Bind(&WaitableEvent::Wait, |
| + Unretained(&waiter_)))); |
| + factory->WaitForAllTasksToRun(); |
| + } |
| + |
| + // Wait for all counters to complete. |
| + for (auto& count_waiter : count_waiters) |
| + count_waiter->Wait(); |
| + |
| + EXPECT_GT(zero_tls_values_, 0); |
| + |
| + // Release tasks waiting on |waiter_|. |
| + waiter_.Signal(); |
| + worker_pool_->WaitForAllWorkersIdleForTesting(); |
| +} |
| + |
| } // namespace internal |
| } // namespace base |