| 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..8c82b5bc665aaaa0de95ff1830a3300e9fb6332f 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,150 @@ 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();
|
| +}
|
| +
|
| +namespace {
|
| +
|
| +class TaskSchedulerWorkerPoolCheckTlsReuse
|
| + : public TaskSchedulerWorkerPoolImplTest {
|
| + public:
|
| + void SetTlsValueAndWait() {
|
| + slot_.Set(reinterpret_cast<void*>(kMagicTlsValue));
|
| + waiter_.Wait();
|
| + }
|
| +
|
| + void CountZeroTlsValuesAndWait(WaitableEvent* count_waiter) {
|
| + 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) {}
|
| + 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));
|
| +
|
| + // 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);
|
| +
|
| + // Release tasks waiting on |waiter_|.
|
| + waiter_.Signal();
|
| + worker_pool_->WaitForAllWorkersIdleForTesting();
|
| +}
|
| +
|
| } // namespace internal
|
| } // namespace base
|
|
|