diff options
author | michaeln@google.com <michaeln@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-07 01:35:37 +0000 |
---|---|---|
committer | michaeln@google.com <michaeln@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-07 01:35:37 +0000 |
commit | 13ecb5e99e5fad75162cad08f1082eff259ff0d9 (patch) | |
tree | 988e3a5dc1b8fc33e4e28dd2826e8c0176350cd4 /base | |
parent | 8c2acc21b17cdcc88edc0bea2623a637c97fdeab (diff) | |
download | chromium_src-13ecb5e99e5fad75162cad08f1082eff259ff0d9.zip chromium_src-13ecb5e99e5fad75162cad08f1082eff259ff0d9.tar.gz chromium_src-13ecb5e99e5fad75162cad08f1082eff259ff0d9.tar.bz2 |
Flush SequenceWorkerPool tasks after each test. Applies to unit_test, interactive_ui test, browser_tests... pretty much all gtest based content library test harnesses are affected.
The CL changes semantics and implementation of the existing FlushForTesting method (which wasn't suitable for this usage).
* The old method would wait for delayed tasks prior to continuing, the new method
will not run them but will delete them prior to continuing.
* The old method would deadlock if called after Shutdown had been called, the new method returns immediately in that case.
A few SWP unittests relied on the waiting for delayed task completion behavior. Those have been modified to explicitly wait thru other means.
BUG=168415,166470
Review URL: https://codereview.chromium.org/11649032
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@186578 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base')
-rw-r--r-- | base/test/sequenced_task_runner_test_template.cc | 13 | ||||
-rw-r--r-- | base/test/sequenced_task_runner_test_template.h | 17 | ||||
-rw-r--r-- | base/test/task_runner_test_template.cc | 14 | ||||
-rw-r--r-- | base/test/task_runner_test_template.h | 14 | ||||
-rw-r--r-- | base/threading/sequenced_worker_pool.cc | 114 | ||||
-rw-r--r-- | base/threading/sequenced_worker_pool.h | 1 | ||||
-rw-r--r-- | base/threading/sequenced_worker_pool_unittest.cc | 81 |
7 files changed, 211 insertions, 43 deletions
diff --git a/base/test/sequenced_task_runner_test_template.cc b/base/test/sequenced_task_runner_test_template.cc index 50487d2..010f439 100644 --- a/base/test/sequenced_task_runner_test_template.cc +++ b/base/test/sequenced_task_runner_test_template.cc @@ -16,7 +16,10 @@ TaskEvent::TaskEvent(int i, Type type) : i(i), type(type) { } -SequencedTaskTracker::SequencedTaskTracker() : next_post_i_(0) { +SequencedTaskTracker::SequencedTaskTracker() + : next_post_i_(0), + task_end_count_(0), + task_end_cv_(&lock_) { } void SequencedTaskTracker::PostWrappedNonNestableTask( @@ -81,6 +84,8 @@ void SequencedTaskTracker::TaskStarted(int i) { void SequencedTaskTracker::TaskEnded(int i) { AutoLock lock(lock_); events_.push_back(TaskEvent(i, TaskEvent::END)); + ++task_end_count_; + task_end_cv_.Signal(); } const std::vector<TaskEvent>& @@ -88,6 +93,12 @@ SequencedTaskTracker::GetTaskEvents() const { return events_; } +void SequencedTaskTracker::WaitForCompletedTasks(int count) { + AutoLock lock(lock_); + while (task_end_count_ < count) + task_end_cv_.Wait(); +} + SequencedTaskTracker::~SequencedTaskTracker() { } diff --git a/base/test/sequenced_task_runner_test_template.h b/base/test/sequenced_task_runner_test_template.h index 0c8d135..761eefd 100644 --- a/base/test/sequenced_task_runner_test_template.h +++ b/base/test/sequenced_task_runner_test_template.h @@ -18,6 +18,7 @@ #include "base/callback.h" #include "base/memory/ref_counted.h" #include "base/sequenced_task_runner.h" +#include "base/synchronization/condition_variable.h" #include "base/synchronization/lock.h" #include "base/time.h" #include "testing/gtest/include/gtest/gtest.h" @@ -61,6 +62,9 @@ class SequencedTaskTracker : public RefCountedThreadSafe<SequencedTaskTracker> { const std::vector<TaskEvent>& GetTaskEvents() const; + // Returns after the tracker observes a total of |count| task completions. + void WaitForCompletedTasks(int count); + private: friend class RefCountedThreadSafe<SequencedTaskTracker>; @@ -79,7 +83,7 @@ class SequencedTaskTracker : public RefCountedThreadSafe<SequencedTaskTracker> { // Records a end event for task |i|. void TaskEnded(int i); - // Protects events_ and next_post_i_. + // Protects events_, next_post_i_, task_end_count_ and task_end_cv_. Lock lock_; // The events as they occurred for each task (protected by lock_). @@ -89,6 +93,10 @@ class SequencedTaskTracker : public RefCountedThreadSafe<SequencedTaskTracker> { // lock_). int next_post_i_; + // The number of task end events we've received. + int task_end_count_; + ConditionVariable task_end_cv_; + DISALLOW_COPY_AND_ASSIGN(SequencedTaskTracker); }; @@ -112,7 +120,7 @@ template <typename TaskRunnerTestDelegate> class SequencedTaskRunnerTest : public testing::Test { protected: SequencedTaskRunnerTest() - : task_tracker_(new internal::SequencedTaskTracker()) {} + : task_tracker_(new internal::SequencedTaskTracker()) {} const scoped_refptr<internal::SequencedTaskTracker> task_tracker_; TaskRunnerTestDelegate delegate_; @@ -191,6 +199,7 @@ TYPED_TEST_P(SequencedTaskRunnerTest, SequentialDelayedNonNestable) { TimeDelta::FromMilliseconds(kDelayIncrementMs * i)); } + this->task_tracker_->WaitForCompletedTasks(kTaskCount); this->delegate_.StopTaskRunner(); EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(), @@ -244,6 +253,7 @@ TYPED_TEST_P(SequencedTaskRunnerTest, DelayedTaskBasic) { Time time_before_run = Time::Now(); this->task_tracker_->PostWrappedDelayedNonNestableTask( task_runner, Closure(), kDelay); + this->task_tracker_->WaitForCompletedTasks(kTaskCount); this->delegate_.StopTaskRunner(); Time time_after_run = Time::Now(); @@ -278,6 +288,7 @@ TYPED_TEST_P(SequencedTaskRunnerTest, DelayedTasksSameDelay) { task_runner, Closure(), kDelay); this->task_tracker_->PostWrappedDelayedNonNestableTask( task_runner, Closure(), kDelay); + this->task_tracker_->WaitForCompletedTasks(kTaskCount); this->delegate_.StopTaskRunner(); EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(), @@ -306,6 +317,7 @@ TYPED_TEST_P(SequencedTaskRunnerTest, DelayedTaskAfterLongTask) { TimeDelta::FromMilliseconds(50))); this->task_tracker_->PostWrappedDelayedNonNestableTask( task_runner, Closure(), TimeDelta::FromMilliseconds(10)); + this->task_tracker_->WaitForCompletedTasks(kTaskCount); this->delegate_.StopTaskRunner(); EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(), @@ -335,6 +347,7 @@ TYPED_TEST_P(SequencedTaskRunnerTest, DelayedTaskAfterManyLongTasks) { } this->task_tracker_->PostWrappedDelayedNonNestableTask( task_runner, Closure(), TimeDelta::FromMilliseconds(10)); + this->task_tracker_->WaitForCompletedTasks(kTaskCount); this->delegate_.StopTaskRunner(); EXPECT_TRUE(CheckNonNestableInvariants(this->task_tracker_->GetTaskEvents(), diff --git a/base/test/task_runner_test_template.cc b/base/test/task_runner_test_template.cc index 239a99f..b756203 100644 --- a/base/test/task_runner_test_template.cc +++ b/base/test/task_runner_test_template.cc @@ -8,7 +8,7 @@ namespace base { namespace internal { -TaskTracker::TaskTracker() {} +TaskTracker::TaskTracker() : task_runs_(0), task_runs_cv_(&lock_) {} TaskTracker::~TaskTracker() {} @@ -17,18 +17,26 @@ Closure TaskTracker::WrapTask(const Closure& task, int i) { } void TaskTracker::RunTask(const Closure& task, int i) { - AutoLock lock(task_run_counts_lock_); + AutoLock lock(lock_); if (!task.is_null()) { task.Run(); } ++task_run_counts_[i]; + ++task_runs_; + task_runs_cv_.Signal(); } std::map<int, int> TaskTracker::GetTaskRunCounts() const { - AutoLock lock(task_run_counts_lock_); + AutoLock lock(lock_); return task_run_counts_; } +void TaskTracker::WaitForCompletedTasks(int count) { + AutoLock lock(lock_); + while (task_runs_ < count) + task_runs_cv_.Wait(); +} + void ExpectRunsTasksOnCurrentThread( bool expected_value, const scoped_refptr<TaskRunner>& task_runner) { diff --git a/base/test/task_runner_test_template.h b/base/test/task_runner_test_template.h index 437a7d9..2dd8814 100644 --- a/base/test/task_runner_test_template.h +++ b/base/test/task_runner_test_template.h @@ -25,7 +25,8 @@ // } // // // Stop the task runner and make sure all tasks posted before -// // this is called are run. +// // this is called are run. Caveat: delayed tasks are not run, + // they're simply deleted. // void StopTaskRunner() { // ... // } @@ -58,6 +59,7 @@ #include "base/bind.h" #include "base/callback.h" #include "base/memory/ref_counted.h" +#include "base/synchronization/condition_variable.h" #include "base/synchronization/lock.h" #include "base/task_runner.h" #include "base/threading/thread.h" @@ -81,6 +83,9 @@ class TaskTracker : public RefCountedThreadSafe<TaskTracker> { std::map<int, int> GetTaskRunCounts() const; + // Returns after the tracker observes a total of |count| task completions. + void WaitForCompletedTasks(int count); + private: friend class RefCountedThreadSafe<TaskTracker>; @@ -88,8 +93,10 @@ class TaskTracker : public RefCountedThreadSafe<TaskTracker> { void RunTask(const Closure& task, int i); - mutable Lock task_run_counts_lock_; + mutable Lock lock_; std::map<int, int> task_run_counts_; + int task_runs_; + ConditionVariable task_runs_cv_; DISALLOW_COPY_AND_ASSIGN(TaskTracker); }; @@ -140,6 +147,7 @@ TYPED_TEST_P(TaskRunnerTest, Delayed) { } std::map<int, int> expected_task_run_counts; + int expected_total_tasks = 0; this->delegate_.StartTaskRunner(); scoped_refptr<TaskRunner> task_runner = this->delegate_.GetTaskRunner(); @@ -150,8 +158,10 @@ TYPED_TEST_P(TaskRunnerTest, Delayed) { task_runner->PostDelayedTask( FROM_HERE, ith_task, base::TimeDelta::FromMilliseconds(j)); ++expected_task_run_counts[i]; + ++expected_total_tasks; } } + this->task_tracker_->WaitForCompletedTasks(expected_total_tasks); this->delegate_.StopTaskRunner(); EXPECT_EQ(expected_task_run_counts, diff --git a/base/threading/sequenced_worker_pool.cc b/base/threading/sequenced_worker_pool.cc index 12b7564..f89a582 100644 --- a/base/threading/sequenced_worker_pool.cc +++ b/base/threading/sequenced_worker_pool.cc @@ -281,7 +281,7 @@ class SequencedWorkerPool::Inner { bool IsRunningSequenceOnCurrentThread(SequenceToken sequence_token) const; - void FlushForTesting(); + void CleanupForTesting(); void SignalHasWorkForTesting(); @@ -299,9 +299,13 @@ class SequencedWorkerPool::Inner { GET_WORK_WAIT, }; - // Returns whether there are no more pending tasks and all threads - // are idle. Must be called under lock. - bool IsIdle() const; + enum CleanupState { + CLEANUP_REQUESTED, + CLEANUP_STARTING, + CLEANUP_RUNNING, + CLEANUP_FINISHING, + CLEANUP_DONE, + }; // Called from within the lock, this converts the given token name into a // token ID, creating a new one if necessary. @@ -334,6 +338,8 @@ class SequencedWorkerPool::Inner { TimeDelta* wait_time, std::vector<Closure>* delete_these_outside_lock); + void HandleCleanup(); + // Peforms init and cleanup around running the given task. WillRun... // returns the value from PrepareToStartAdditionalThreadIfNecessary. // The calling code should call FinishStartingAdditionalThread once the @@ -389,10 +395,6 @@ class SequencedWorkerPool::Inner { ConditionVariable has_work_cv_; // Condition variable that is waited on by non-worker threads (in - // FlushForTesting()) until IsIdle() goes to true. - ConditionVariable is_idle_cv_; - - // Condition variable that is waited on by non-worker threads (in // Shutdown()) until CanShutdown() goes to true. ConditionVariable can_shutdown_cv_; @@ -450,6 +452,11 @@ class SequencedWorkerPool::Inner { // has been called. int max_blocking_tasks_after_shutdown_; + // State used to cleanup for testing, all guarded by lock_. + CleanupState cleanup_state_; + size_t cleanup_idlers_; + ConditionVariable cleanup_cv_; + TestingObserver* const testing_observer_; DISALLOW_COPY_AND_ASSIGN(Inner); @@ -493,7 +500,6 @@ SequencedWorkerPool::Inner::Inner( last_sequence_number_(0), lock_(), has_work_cv_(&lock_), - is_idle_cv_(&lock_), can_shutdown_cv_(&lock_), max_threads_(max_threads), thread_name_prefix_(thread_name_prefix), @@ -505,6 +511,9 @@ SequencedWorkerPool::Inner::Inner( trace_id_(0), shutdown_called_(false), max_blocking_tasks_after_shutdown_(0), + cleanup_state_(CLEANUP_DONE), + cleanup_idlers_(0), + cleanup_cv_(&lock_), testing_observer_(observer) {} SequencedWorkerPool::Inner::~Inner() { @@ -609,10 +618,21 @@ bool SequencedWorkerPool::Inner::IsRunningSequenceOnCurrentThread( return found->second->running_sequence().Equals(sequence_token); } -void SequencedWorkerPool::Inner::FlushForTesting() { +// See https://code.google.com/p/chromium/issues/detail?id=168415 +void SequencedWorkerPool::Inner::CleanupForTesting() { + DCHECK(!RunsTasksOnCurrentThread()); + base::ThreadRestrictions::ScopedAllowWait allow_wait; AutoLock lock(lock_); - while (!IsIdle()) - is_idle_cv_.Wait(); + CHECK_EQ(CLEANUP_DONE, cleanup_state_); + if (shutdown_called_) + return; + if (pending_tasks_.empty() && waiting_thread_count_ == threads_.size()) + return; + cleanup_state_ = CLEANUP_REQUESTED; + cleanup_idlers_ = 0; + has_work_cv_.Signal(); + while (cleanup_state_ != CLEANUP_DONE) + cleanup_cv_.Wait(); } void SequencedWorkerPool::Inner::SignalHasWorkForTesting() { @@ -624,7 +644,8 @@ void SequencedWorkerPool::Inner::Shutdown( DCHECK_GE(max_new_blocking_tasks_after_shutdown, 0); { AutoLock lock(lock_); - + // Cleanup and Shutdown should not be called concurrently. + CHECK_EQ(CLEANUP_DONE, cleanup_state_); if (shutdown_called_) return; shutdown_called_ = true; @@ -672,6 +693,8 @@ void SequencedWorkerPool::Inner::ThreadLoop(Worker* this_worker) { base::mac::ScopedNSAutoreleasePool autorelease_pool; #endif + HandleCleanup(); + // See GetWork for what delete_these_outside_lock is doing. SequencedTask task; TimeDelta wait_time; @@ -720,6 +743,21 @@ void SequencedWorkerPool::Inner::ThreadLoop(Worker* this_worker) { SequenceToken(), CONTINUE_ON_SHUTDOWN); } DidRunWorkerTask(task); // Must be done inside the lock. + } else if (cleanup_state_ == CLEANUP_RUNNING) { + switch (status) { + case GET_WORK_WAIT: { + AutoUnlock unlock(lock_); + delete_these_outside_lock.clear(); + } + break; + case GET_WORK_NOT_FOUND: + CHECK(delete_these_outside_lock.empty()); + cleanup_state_ = CLEANUP_FINISHING; + cleanup_cv_.Broadcast(); + break; + default: + NOTREACHED(); + } } else { // When we're terminating and there's no more work, we can // shut down, other workers can complete any pending or new tasks. @@ -733,9 +771,6 @@ void SequencedWorkerPool::Inner::ThreadLoop(Worker* this_worker) { blocking_shutdown_pending_task_count_ == 0) break; waiting_thread_count_++; - // This is the only time that IsIdle() can go to true. - if (IsIdle()) - is_idle_cv_.Signal(); switch (status) { case GET_WORK_NOT_FOUND: @@ -760,9 +795,44 @@ void SequencedWorkerPool::Inner::ThreadLoop(Worker* this_worker) { can_shutdown_cv_.Signal(); } -bool SequencedWorkerPool::Inner::IsIdle() const { +void SequencedWorkerPool::Inner::HandleCleanup() { lock_.AssertAcquired(); - return pending_tasks_.empty() && waiting_thread_count_ == threads_.size(); + if (cleanup_state_ == CLEANUP_DONE) + return; + if (cleanup_state_ == CLEANUP_REQUESTED) { + // We win, we get to do the cleanup as soon as the others wise up and idle. + cleanup_state_ = CLEANUP_STARTING; + while (thread_being_created_ || + cleanup_idlers_ != threads_.size() - 1) { + has_work_cv_.Signal(); + cleanup_cv_.Wait(); + } + cleanup_state_ = CLEANUP_RUNNING; + return; + } + if (cleanup_state_ == CLEANUP_STARTING) { + // Another worker thread is cleaning up, we idle here until thats done. + ++cleanup_idlers_; + cleanup_cv_.Broadcast(); + while (cleanup_state_ != CLEANUP_FINISHING) { + cleanup_cv_.Wait(); + } + --cleanup_idlers_; + cleanup_cv_.Broadcast(); + return; + } + if (cleanup_state_ == CLEANUP_FINISHING) { + // We wait for all idlers to wake up prior to being DONE. + while (cleanup_idlers_ != 0) { + cleanup_cv_.Broadcast(); + cleanup_cv_.Wait(); + } + if (cleanup_state_ == CLEANUP_FINISHING) { + cleanup_state_ = CLEANUP_DONE; + cleanup_cv_.Signal(); + } + return; + } } int SequencedWorkerPool::Inner::LockedGetNamedTokenID( @@ -866,6 +936,11 @@ SequencedWorkerPool::Inner::GetWorkStatus SequencedWorkerPool::Inner::GetWork( // The time to run has not come yet. *wait_time = i->time_to_run - current_time; status = GET_WORK_WAIT; + if (cleanup_state_ == CLEANUP_RUNNING) { + // Deferred tasks are deleted when cleaning up, see Inner::ThreadLoop. + delete_these_outside_lock->push_back(i->task); + pending_tasks_.erase(i); + } break; } @@ -972,6 +1047,7 @@ int SequencedWorkerPool::Inner::PrepareToStartAdditionalThreadIfHelpful() { // shutdown call. if (!shutdown_called_ && !thread_being_created_ && + cleanup_state_ == CLEANUP_DONE && threads_.size() < max_threads_ && waiting_thread_count_ == 0) { // We could use an additional thread if there's work to be done. @@ -1150,7 +1226,7 @@ bool SequencedWorkerPool::IsRunningSequenceOnCurrentThread( } void SequencedWorkerPool::FlushForTesting() { - inner_->FlushForTesting(); + inner_->CleanupForTesting(); } void SequencedWorkerPool::SignalHasWorkForTesting() { diff --git a/base/threading/sequenced_worker_pool.h b/base/threading/sequenced_worker_pool.h index 9c7108e..a96cd76 100644 --- a/base/threading/sequenced_worker_pool.h +++ b/base/threading/sequenced_worker_pool.h @@ -288,6 +288,7 @@ class BASE_EXPORT SequencedWorkerPool : public TaskRunner { // Blocks until all pending tasks are complete. This should only be called in // unit tests when you want to validate something that should have happened. + // This will not flush delayed tasks; delayed tasks get deleted. // // Note that calling this will not prevent other threads from posting work to // the queue while the calling thread is waiting on Flush(). In this case, diff --git a/base/threading/sequenced_worker_pool_unittest.cc b/base/threading/sequenced_worker_pool_unittest.cc index 74d23fc..f0caf1a 100644 --- a/base/threading/sequenced_worker_pool_unittest.cc +++ b/base/threading/sequenced_worker_pool_unittest.cc @@ -99,16 +99,18 @@ class TestTracker : public base::RefCountedThreadSafe<TestTracker> { SignalWorkerDone(id); } - void PostAdditionalTasks(int id, SequencedWorkerPool* pool) { + void PostAdditionalTasks( + int id, SequencedWorkerPool* pool, + bool expected_return_value) { Closure fast_task = base::Bind(&TestTracker::FastTask, this, 100); - EXPECT_FALSE( - pool->PostWorkerTaskWithShutdownBehavior( - FROM_HERE, fast_task, - SequencedWorkerPool::CONTINUE_ON_SHUTDOWN)); - EXPECT_FALSE( - pool->PostWorkerTaskWithShutdownBehavior( - FROM_HERE, fast_task, - SequencedWorkerPool::SKIP_ON_SHUTDOWN)); + EXPECT_EQ(expected_return_value, + pool->PostWorkerTaskWithShutdownBehavior( + FROM_HERE, fast_task, + SequencedWorkerPool::CONTINUE_ON_SHUTDOWN)); + EXPECT_EQ(expected_return_value, + pool->PostWorkerTaskWithShutdownBehavior( + FROM_HERE, fast_task, + SequencedWorkerPool::SKIP_ON_SHUTDOWN)); pool->PostWorkerTaskWithShutdownBehavior( FROM_HERE, fast_task, SequencedWorkerPool::BLOCK_SHUTDOWN); @@ -139,6 +141,11 @@ class TestTracker : public base::RefCountedThreadSafe<TestTracker> { return ret; } + size_t GetTasksCompletedCount() { + base::AutoLock lock(lock_); + return complete_sequence_.size(); + } + void ClearCompleteSequence() { base::AutoLock lock(lock_); complete_sequence_.clear(); @@ -517,7 +524,8 @@ TEST_F(SequencedWorkerPoolTest, AllowsAfterShutdown) { for (int i = 0; i < kNumQueuedTasks; ++i) { EXPECT_TRUE(pool()->PostWorkerTaskWithShutdownBehavior( FROM_HERE, - base::Bind(&TestTracker::PostAdditionalTasks, tracker(), i, pool()), + base::Bind(&TestTracker::PostAdditionalTasks, tracker(), i, pool(), + false), SequencedWorkerPool::BLOCK_SHUTDOWN)); } @@ -749,6 +757,47 @@ TEST_F(SequencedWorkerPoolTest, IsRunningOnCurrentThread) { unused_pool->Shutdown(); } +// Verify that FlushForTesting works as intended. +TEST_F(SequencedWorkerPoolTest, FlushForTesting) { + // Should be fine to call on a new instance. + pool()->FlushForTesting(); + + // Queue up a bunch of work, including a long delayed task and + // a task that produces additional tasks as an artifact. + pool()->PostDelayedWorkerTask( + FROM_HERE, + base::Bind(&TestTracker::FastTask, tracker(), 0), + TimeDelta::FromMinutes(5)); + pool()->PostWorkerTask(FROM_HERE, + base::Bind(&TestTracker::SlowTask, tracker(), 0)); + const size_t kNumFastTasks = 20; + for (size_t i = 0; i < kNumFastTasks; i++) { + pool()->PostWorkerTask(FROM_HERE, + base::Bind(&TestTracker::FastTask, tracker(), 0)); + } + pool()->PostWorkerTask( + FROM_HERE, + base::Bind(&TestTracker::PostAdditionalTasks, tracker(), 0, pool(), + true)); + + // We expect all except the delayed task to have been run. We verify all + // closures have been deleted deleted by looking at the refcount of the + // tracker. + EXPECT_FALSE(tracker()->HasOneRef()); + pool()->FlushForTesting(); + EXPECT_TRUE(tracker()->HasOneRef()); + EXPECT_EQ(1 + kNumFastTasks + 1 + 3, tracker()->GetTasksCompletedCount()); + + // Should be fine to call on an idle instance with all threads created, and + // spamming the method shouldn't deadlock or confuse the class. + pool()->FlushForTesting(); + pool()->FlushForTesting(); + + // Should be fine to call after shutdown too. + pool()->Shutdown(); + pool()->FlushForTesting(); +} + class SequencedWorkerPoolTaskRunnerTestDelegate { public: SequencedWorkerPoolTaskRunnerTestDelegate() {} @@ -765,8 +814,8 @@ class SequencedWorkerPoolTaskRunnerTestDelegate { } void StopTaskRunner() { - // Make sure all tasks (including delayed ones) are run before shutting - // down. + // Make sure all tasks are run before shutting down. Delayed tasks are + // not run, they're simply deleted. pool_owner_->pool()->FlushForTesting(); pool_owner_->pool()->Shutdown(); // Don't reset |pool_owner_| here, as the test may still hold a @@ -805,8 +854,8 @@ class SequencedWorkerPoolTaskRunnerWithShutdownBehaviorTestDelegate { } void StopTaskRunner() { - // Make sure all tasks (including delayed ones) are run before shutting - // down. + // Make sure all tasks are run before shutting down. Delayed tasks are + // not run, they're simply deleted. pool_owner_->pool()->FlushForTesting(); pool_owner_->pool()->Shutdown(); // Don't reset |pool_owner_| here, as the test may still hold a @@ -846,8 +895,8 @@ class SequencedWorkerPoolSequencedTaskRunnerTestDelegate { } void StopTaskRunner() { - // Make sure all tasks (including delayed ones) are run before shutting - // down. + // Make sure all tasks are run before shutting down. Delayed tasks are + // not run, they're simply deleted. pool_owner_->pool()->FlushForTesting(); pool_owner_->pool()->Shutdown(); // Don't reset |pool_owner_| here, as the test may still hold a |