Index: base/threading/sequenced_worker_pool_unittest.cc |
diff --git a/base/threading/sequenced_worker_pool_unittest.cc b/base/threading/sequenced_worker_pool_unittest.cc |
index ed5f89694983ca707b977990a59982ca5a9fb83b..9d0f607775d47e4023bc0918f0f125dca61dc816 100644 |
--- a/base/threading/sequenced_worker_pool_unittest.cc |
+++ b/base/threading/sequenced_worker_pool_unittest.cc |
@@ -67,6 +67,23 @@ class ThreadBlocker { |
size_t unblock_counter_; |
}; |
+class DestructionDeadlockChecker |
+ : public base::RefCountedThreadSafe<DestructionDeadlockChecker> { |
+ public: |
+ DestructionDeadlockChecker(const scoped_refptr<SequencedWorkerPool>& pool) |
+ : pool_(pool) {} |
+ |
+ protected: |
+ virtual ~DestructionDeadlockChecker() { |
+ // This method should not deadlock. |
+ pool_->RunsTasksOnCurrentThread(); |
+ } |
+ |
+ private: |
+ scoped_refptr<SequencedWorkerPool> pool_; |
+ friend class base::RefCountedThreadSafe<DestructionDeadlockChecker>; |
+}; |
+ |
class TestTracker : public base::RefCountedThreadSafe<TestTracker> { |
public: |
TestTracker() |
@@ -117,6 +134,20 @@ class TestTracker : public base::RefCountedThreadSafe<TestTracker> { |
SignalWorkerDone(id); |
} |
+ // This task posts itself back onto the SequencedWorkerPool before it |
+ // finishes running. Each instance of the task maintains a strong reference |
+ // to a DestructionDeadlockChecker. The DestructionDeadlockChecker is only |
+ // destroyed when the task is destroyed without being run, which only happens |
+ // during destruction of the SequencedWorkerPool. |
+ void PostRepostingTask( |
+ const scoped_refptr<SequencedWorkerPool>& pool, |
+ const scoped_refptr<DestructionDeadlockChecker>& checker) { |
+ Closure reposting_task = |
+ base::Bind(&TestTracker::PostRepostingTask, this, pool, checker); |
+ pool->PostWorkerTaskWithShutdownBehavior( |
+ FROM_HERE, reposting_task, SequencedWorkerPool::SKIP_ON_SHUTDOWN); |
+ } |
+ |
// Waits until the given number of tasks have started executing. |
void WaitUntilTasksBlocked(size_t count) { |
{ |
@@ -749,6 +780,21 @@ TEST_F(SequencedWorkerPoolTest, IsRunningOnCurrentThread) { |
unused_pool->Shutdown(); |
} |
+// Checks that tasks are destroyed in the right context during shutdown. If a |
+// task is destroyed while SequencedWorkerPool's global lock is held, |
+// SequencedWorkerPool might deadlock. |
+TEST_F(SequencedWorkerPoolTest, AvoidsDeadlockOnShutdown) { |
+ for (int i = 0; i < 4; ++i) { |
+ scoped_refptr<DestructionDeadlockChecker> checker( |
+ new DestructionDeadlockChecker(pool())); |
+ tracker()->PostRepostingTask(pool(), checker); |
+ } |
+ |
+ // Shutting down the pool should destroy the DestructionDeadlockCheckers, |
+ // which in turn should not deadlock in their destructors. |
+ pool()->Shutdown(); |
+} |
+ |
// Verify that FlushForTesting works as intended. |
TEST_F(SequencedWorkerPoolTest, FlushForTesting) { |
// Should be fine to call on a new instance. |