Index: chrome/browser/sync/glue/sync_backend_registrar_unittest.cc |
diff --git a/chrome/browser/sync/glue/sync_backend_registrar_unittest.cc b/chrome/browser/sync/glue/sync_backend_registrar_unittest.cc |
index cd2d29ddf4edfdb88cc9ecefb6feab6cab9e8586..0bb25faf43a729543b1f44dd9a395cd1c0a3aed0 100644 |
--- a/chrome/browser/sync/glue/sync_backend_registrar_unittest.cc |
+++ b/chrome/browser/sync/glue/sync_backend_registrar_unittest.cc |
@@ -256,6 +256,91 @@ TEST_F(SyncBackendRegistrarTest, ActivateDeactivateNonUIDataType) { |
TriggerChanges(registrar_.get(), AUTOFILL); |
} |
+class SyncBackendRegistrarShutdownTest : public testing::Test { |
+ public: |
+ void BlockDBThread() { |
+ EXPECT_FALSE(db_thread_lock_.Try()); |
+ |
+ db_thread_blocked_.Signal(); |
+ base::AutoLock l(db_thread_lock_); |
+ } |
+ |
+ protected: |
+ friend class TestRegistrar; |
+ |
+ SyncBackendRegistrarShutdownTest() |
+ : thread_bundle_(content::TestBrowserThreadBundle::REAL_DB_THREAD | |
+ content::TestBrowserThreadBundle::REAL_FILE_THREAD | |
+ content::TestBrowserThreadBundle::REAL_IO_THREAD), |
+ db_thread_blocked_(false, false), |
+ registrar_destroyed_(false, false) {} |
+ |
+ virtual ~SyncBackendRegistrarShutdownTest() {} |
+ |
+ content::TestBrowserThreadBundle thread_bundle_; |
+ base::WaitableEvent db_thread_blocked_; |
+ base::Lock db_thread_lock_; |
+ base::WaitableEvent registrar_destroyed_; |
+}; |
+ |
+// Wrap SyncBackendRegistrar so that we can monitor its lifetime. |
+class TestRegistrar : public SyncBackendRegistrar { |
+ public: |
+ explicit TestRegistrar(Profile* profile, |
+ SyncBackendRegistrarShutdownTest* test) |
+ : SyncBackendRegistrar("test", profile, scoped_ptr<base::Thread>()), |
+ test_(test) {} |
+ |
+ virtual ~TestRegistrar() { test_->registrar_destroyed_.Signal(); } |
+ |
+ private: |
+ SyncBackendRegistrarShutdownTest* test_; |
+}; |
+ |
+TEST_F(SyncBackendRegistrarShutdownTest, BlockingShutdown) { |
+ // Take ownership of |db_thread_lock_| so that the DB thread can't acquire it. |
+ db_thread_lock_.Acquire(); |
+ |
+ // This will block the DB thread by waiting on |db_thread_lock_|. |
+ BrowserThread::PostTask( |
+ BrowserThread::DB, |
+ FROM_HERE, |
+ base::Bind(&SyncBackendRegistrarShutdownTest::BlockDBThread, |
+ base::Unretained(this))); |
+ |
+ TestingProfile profile; |
+ scoped_ptr<TestRegistrar> registrar(new TestRegistrar(&profile, this)); |
+ base::Thread* sync_thread = registrar->sync_thread(); |
+ |
+ // Stop here until the DB thread gets a chance to run and block on the lock. |
+ // Please note that since the task above didn't finish, the task to |
+ // initialize the worker on the DB thread hasn't had a chance to run yet too. |
+ // Which means ModelSafeWorker::SetWorkingLoopToCurrent hasn't been called |
+ // for the DB worker. |
+ db_thread_blocked_.Wait(); |
+ |
+ registrar->SetInitialTypes(ModelTypeSet()); |
+ |
+ // Start the shutdown. |
+ registrar->RequestWorkerStopOnUIThread(); |
+ sync_thread->message_loop()->PostTask( |
+ FROM_HERE, |
+ base::Bind(&SyncBackendRegistrar::Shutdown, |
+ base::Unretained(registrar.release()))); |
+ |
+ // The test verifies that the sync thread doesn't block because |
+ // of the blocked DB thread and can finish the shutdown. |
+ sync_thread->message_loop()->RunUntilIdle(); |
+ |
+ db_thread_lock_.Release(); |
+ |
+ base::MessageLoop::current()->RunUntilIdle(); |
+ |
+ // This verifies that all workers have been removed and the registrar |
+ // destroyed. |
+ registrar_destroyed_.Wait(); |
+} |
+ |
} // namespace |
} // namespace browser_sync |