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 |