From 6f3a70f316f2f3dcde5b3bde5fb258c556c46da6 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Wed, 16 Nov 2016 13:58:05 -0800 Subject: [PATCH] ART: Change ThreadPool::Wait behavior When a pool is in the stopped state, Wait() will not wait for all tasks to complete. Bug: 31385354 Test: m test-art-host-gtest-thread_pool_test Change-Id: Id0144b685ee2fddf1a1c2c2ca334251130121033 --- runtime/thread_pool.cc | 6 +++--- runtime/thread_pool.h | 7 ++++++- runtime/thread_pool_test.cc | 18 ++++++++++++++++++ 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/runtime/thread_pool.cc b/runtime/thread_pool.cc index b14f340c4..65fd99985 100644 --- a/runtime/thread_pool.cc +++ b/runtime/thread_pool.cc @@ -177,7 +177,7 @@ Task* ThreadPool::GetTask(Thread* self) { } ++waiting_count_; - if (waiting_count_ == GetThreadCount() && tasks_.empty()) { + if (waiting_count_ == GetThreadCount() && !HasOutstandingTasks()) { // We may be done, lets broadcast to the completion condition. completion_condition_.Broadcast(self); } @@ -200,7 +200,7 @@ Task* ThreadPool::TryGetTask(Thread* self) { } Task* ThreadPool::TryGetTaskLocked() { - if (started_ && !tasks_.empty()) { + if (HasOutstandingTasks()) { Task* task = tasks_.front(); tasks_.pop_front(); return task; @@ -218,7 +218,7 @@ void ThreadPool::Wait(Thread* self, bool do_work, bool may_hold_locks) { } // Wait until each thread is waiting and the task list is empty. MutexLock mu(self, task_queue_lock_); - while (!shutting_down_ && (waiting_count_ != GetThreadCount() || !tasks_.empty())) { + while (!shutting_down_ && (waiting_count_ != GetThreadCount() || HasOutstandingTasks())) { if (!may_hold_locks) { completion_condition_.Wait(self); } else { diff --git a/runtime/thread_pool.h b/runtime/thread_pool.h index b6c6f02db..2ff33a605 100644 --- a/runtime/thread_pool.h +++ b/runtime/thread_pool.h @@ -100,7 +100,8 @@ class ThreadPool { ThreadPool(const char* name, size_t num_threads); virtual ~ThreadPool(); - // Wait for all tasks currently on queue to get completed. + // Wait for all tasks currently on queue to get completed. If the pool has been stopped, only + // wait till all already running tasks are done. void Wait(Thread* self, bool do_work, bool may_hold_locks) REQUIRES(!task_queue_lock_); size_t GetTaskCount(Thread* self) REQUIRES(!task_queue_lock_); @@ -130,6 +131,10 @@ class ThreadPool { return shutting_down_; } + bool HasOutstandingTasks() const REQUIRES(task_queue_lock_) { + return started_ && !tasks_.empty(); + } + const std::string name_; Mutex task_queue_lock_; ConditionVariable task_queue_condition_ GUARDED_BY(task_queue_lock_); diff --git a/runtime/thread_pool_test.cc b/runtime/thread_pool_test.cc index d5f17d17b..2ae2ecff4 100644 --- a/runtime/thread_pool_test.cc +++ b/runtime/thread_pool_test.cc @@ -98,6 +98,24 @@ TEST_F(ThreadPoolTest, StopStart) { thread_pool.Wait(self, false, false); } +TEST_F(ThreadPoolTest, StopWait) { + Thread* self = Thread::Current(); + ThreadPool thread_pool("Thread pool test thread pool", num_threads); + + AtomicInteger count(0); + static const int32_t num_tasks = num_threads * 100; + for (int32_t i = 0; i < num_tasks; ++i) { + thread_pool.AddTask(self, new CountTask(&count)); + } + + // Signal the threads to start processing tasks. + thread_pool.StartWorkers(self); + usleep(200); + thread_pool.StopWorkers(self); + + thread_pool.Wait(self, false, false); // We should not deadlock here. +} + class TreeTask : public Task { public: TreeTask(ThreadPool* const thread_pool, AtomicInteger* count, int depth) -- 2.11.0