diff options
-rw-r--r-- | chrome/browser/sync/engine/all_status.cc | 14 | ||||
-rw-r--r-- | chrome/browser/sync/engine/syncapi.cc | 5 | ||||
-rw-r--r-- | chrome/browser/sync/engine/syncapi.h | 6 | ||||
-rw-r--r-- | chrome/browser/sync/engine/syncer_proto_util.cc | 7 | ||||
-rw-r--r-- | chrome/browser/sync/engine/syncer_thread.cc | 27 | ||||
-rw-r--r-- | chrome/browser/sync/engine/syncer_thread.h | 7 | ||||
-rw-r--r-- | chrome/browser/sync/engine/syncer_thread_unittest.cc | 48 | ||||
-rw-r--r-- | chrome/browser/sync/engine/syncer_types.h | 9 | ||||
-rw-r--r-- | chrome/browser/sync/engine/syncer_unittest.cc | 2 | ||||
-rw-r--r-- | chrome/browser/sync/glue/sync_backend_host.cc | 9 | ||||
-rw-r--r-- | chrome/browser/sync/glue/sync_backend_host.h | 7 | ||||
-rw-r--r-- | chrome/browser/sync/profile_sync_service.cc | 14 | ||||
-rw-r--r-- | chrome/browser/sync/profile_sync_service.h | 1 | ||||
-rw-r--r-- | chrome/browser/sync/sessions/sync_session.h | 6 | ||||
-rw-r--r-- | chrome/browser/sync/sessions/sync_session_unittest.cc | 3 | ||||
-rw-r--r-- | chrome/test/sync/engine/mock_connection_manager.h | 4 | ||||
-rw-r--r-- | chrome/test/sync/engine/syncer_command_test.h | 3 |
17 files changed, 147 insertions, 25 deletions
diff --git a/chrome/browser/sync/engine/all_status.cc b/chrome/browser/sync/engine/all_status.cc index 6bf37d7..6c48905 100644 --- a/chrome/browser/sync/engine/all_status.cc +++ b/chrome/browser/sync/engine/all_status.cc @@ -207,20 +207,14 @@ void AllStatus::HandleChannelEvent(const SyncerEvent& event) { lock.NotifyOverQuota(); break; case SyncerEvent::REQUEST_SYNC_NUDGE: - lock.set_notify_plan(DONT_NOTIFY); - break; case SyncerEvent::PAUSED: - lock.set_notify_plan(DONT_NOTIFY); - break; case SyncerEvent::RESUMED: - lock.set_notify_plan(DONT_NOTIFY); - break; case SyncerEvent::WAITING_FOR_CONNECTION: - lock.set_notify_plan(DONT_NOTIFY); - break; case SyncerEvent::CONNECTED: - lock.set_notify_plan(DONT_NOTIFY); - break; + case SyncerEvent::STOP_SYNCING_PERMANENTLY: + case SyncerEvent::SYNCER_THREAD_EXITING: + lock.set_notify_plan(DONT_NOTIFY); + break; default: LOG(ERROR) << "Unrecognized Syncer Event: " << event.what_happened; lock.set_notify_plan(DONT_NOTIFY); diff --git a/chrome/browser/sync/engine/syncapi.cc b/chrome/browser/sync/engine/syncapi.cc index e17d652..390d9f5 100644 --- a/chrome/browser/sync/engine/syncapi.cc +++ b/chrome/browser/sync/engine/syncapi.cc @@ -1869,6 +1869,11 @@ void SyncManager::SyncInternal::HandleChannelEvent(const SyncerEvent& event) { observer_->OnResumed(); return; } + + if (event.what_happened == SyncerEvent::STOP_SYNCING_PERMANENTLY) { + observer_->OnStopSyncingPermanently(); + return; + } } void SyncManager::SyncInternal::HandleAuthWatcherEvent( diff --git a/chrome/browser/sync/engine/syncapi.h b/chrome/browser/sync/engine/syncapi.h index 7f84abe..4504364 100644 --- a/chrome/browser/sync/engine/syncapi.h +++ b/chrome/browser/sync/engine/syncapi.h @@ -662,6 +662,12 @@ class SyncManager { // The syncer thread has been resumed. virtual void OnResumed() = 0; + // We are no longer permitted to communicate with the server. Sync should + // be disabled and state cleaned up at once. This can happen for a number + // of reasons, e.g. swapping from a test instance to production, or a + // global stop syncing operation has wiped the store. + virtual void OnStopSyncingPermanently() = 0; + private: DISALLOW_COPY_AND_ASSIGN(Observer); }; diff --git a/chrome/browser/sync/engine/syncer_proto_util.cc b/chrome/browser/sync/engine/syncer_proto_util.cc index 76e163a..ec95bc5 100644 --- a/chrome/browser/sync/engine/syncer_proto_util.cc +++ b/chrome/browser/sync/engine/syncer_proto_util.cc @@ -83,6 +83,9 @@ bool SyncerProtoUtil::VerifyResponseBirthday(syncable::Directory* dir, std::string local_birthday = dir->store_birthday(); + // TODO(tim): Bug 46807. Check for new response code denoting a clear store + // operation is in progress. + if (local_birthday.empty()) { if (!response->has_store_birthday()) { LOG(WARNING) << "Expected a birthday on first sync."; @@ -193,6 +196,7 @@ bool SyncerProtoUtil::PostClientToServerMessage( // TODO(ncarter): Add a unit test for the case where the syncer becomes // stuck due to a bad birthday. session->status_controller()->set_syncer_stuck(true); + session->delegate()->OnShouldStopSyncingPermanently(); return false; } @@ -200,9 +204,6 @@ bool SyncerProtoUtil::PostClientToServerMessage( case ClientToServerResponse::SUCCESS: LogResponseProfilingData(*response); return true; - case ClientToServerResponse::NOT_MY_BIRTHDAY: - LOG(WARNING) << "Server thought we had wrong birthday."; - return false; case ClientToServerResponse::THROTTLED: LOG(WARNING) << "Client silenced by server."; session->delegate()->OnSilencedUntil(base::TimeTicks::Now() + diff --git a/chrome/browser/sync/engine/syncer_thread.cc b/chrome/browser/sync/engine/syncer_thread.cc index 1437dfd..ffc7bcd 100644 --- a/chrome/browser/sync/engine/syncer_thread.cc +++ b/chrome/browser/sync/engine/syncer_thread.cc @@ -128,6 +128,14 @@ bool SyncerThread::Start() { // Stop processing. A max wait of at least 2*server RTT time is recommended. // Returns true if we stopped, false otherwise. bool SyncerThread::Stop(int max_wait) { + RequestSyncerExitAndSetThreadStopConditions(); + + // This will join, and finish when ThreadMain terminates. + thread_.Stop(); + return true; +} + +void SyncerThread::RequestSyncerExitAndSetThreadStopConditions() { { AutoLock lock(lock_); // If the thread has been started, then we either already have or are about @@ -135,7 +143,7 @@ bool SyncerThread::Stop(int max_wait) { // it to finish. If the thread has not been started --and we now own the // lock-- then we can early out because the caller has not called Start(). if (!thread_.IsRunning()) - return true; + return; LOG(INFO) << "SyncerThread::Stop - setting ThreadMain exit condition to " << "true (vault_.stop_syncer_thread_)"; @@ -155,10 +163,6 @@ bool SyncerThread::Stop(int max_wait) { // end of ThreadMain. vault_field_changed_.Broadcast(); } - - // This will join, and finish when ThreadMain terminates. - thread_.Stop(); - return true; } bool SyncerThread::RequestPause() { @@ -233,6 +237,13 @@ bool SyncerThread::IsSyncingCurrentlySilenced() { return ret; } +void SyncerThread::OnShouldStopSyncingPermanently() { + RequestSyncerExitAndSetThreadStopConditions(); + + SyncerEvent event(SyncerEvent::STOP_SYNCING_PERMANENTLY); + relay_channel()->Notify(event); +} + void SyncerThread::ThreadMainLoop() { // This is called with lock_ acquired. lock_.AssertAcquired(); @@ -386,14 +397,18 @@ void SyncerThread::PauseUntilResumedOrQuit() { } void SyncerThread::EnterPausedState() { + lock_.AssertAcquired(); vault_.pause_requested_ = false; vault_.paused_ = true; + vault_field_changed_.Broadcast(); SyncerEvent event(SyncerEvent::PAUSED); relay_channel()->Notify(event); } void SyncerThread::ExitPausedState() { + lock_.AssertAcquired(); vault_.paused_ = false; + vault_field_changed_.Broadcast(); SyncerEvent event(SyncerEvent::RESUMED); relay_channel()->Notify(event); } @@ -488,6 +503,8 @@ void SyncerThread::ThreadMain() { thread_main_started_.Signal(); ThreadMainLoop(); LOG(INFO) << "Syncer thread ThreadMain is done."; + SyncerEvent event(SyncerEvent::SYNCER_THREAD_EXITING); + relay_channel()->Notify(event); } void SyncerThread::SyncMain(Syncer* syncer) { diff --git a/chrome/browser/sync/engine/syncer_thread.h b/chrome/browser/sync/engine/syncer_thread.h index 29a2c13..a54ff7f 100644 --- a/chrome/browser/sync/engine/syncer_thread.h +++ b/chrome/browser/sync/engine/syncer_thread.h @@ -57,6 +57,7 @@ class SyncerThread : public base::RefCountedThreadSafe<SyncerThread>, FRIEND_TEST_ALL_PREFIXES(SyncerThreadWithSyncerTest, Pause); FRIEND_TEST_ALL_PREFIXES(SyncerThreadWithSyncerTest, StartWhenNotConnected); FRIEND_TEST_ALL_PREFIXES(SyncerThreadWithSyncerTest, PauseWhenNotConnected); + FRIEND_TEST_ALL_PREFIXES(SyncerThreadWithSyncerTest, StopSyncPermanently); friend class SyncerThreadWithSyncerTest; friend class SyncerThreadFactory; public: @@ -236,6 +237,7 @@ class SyncerThread : public base::RefCountedThreadSafe<SyncerThread>, const base::TimeDelta& new_interval); virtual void OnReceivedLongPollIntervalUpdate( const base::TimeDelta& new_interval); + virtual void OnShouldStopSyncingPermanently(); void HandleServerConnectionEvent(const ServerConnectionEvent& event); @@ -288,6 +290,11 @@ class SyncerThread : public base::RefCountedThreadSafe<SyncerThread>, // For unit tests only. virtual void DisableIdleDetection() { disable_idle_detection_ = true; } + // This sets all conditions for syncer thread termination but does not + // actually join threads. It is expected that Stop will be called at some + // time after to fully stop and clean up. + void RequestSyncerExitAndSetThreadStopConditions(); + // State of the notification framework is tracked by these values. bool p2p_authenticated_; bool p2p_subscribed_; diff --git a/chrome/browser/sync/engine/syncer_thread_unittest.cc b/chrome/browser/sync/engine/syncer_thread_unittest.cc index 79e2665..a9798ad 100644 --- a/chrome/browser/sync/engine/syncer_thread_unittest.cc +++ b/chrome/browser/sync/engine/syncer_thread_unittest.cc @@ -716,6 +716,43 @@ TEST_F(SyncerThreadWithSyncerTest, Throttling) { EXPECT_TRUE(syncer_thread()->Stop(2000)); } +TEST_F(SyncerThreadWithSyncerTest, StopSyncPermanently) { + // The SyncerThread should request an exit from the Syncer and set + // conditions for termination. + const TimeDelta poll_interval = TimeDelta::FromMilliseconds(10); + syncer_thread()->SetSyncerShortPollInterval(poll_interval); + + ListenerMock listener; + WaitableEvent sync_cycle_ended_event(false, false); + WaitableEvent syncer_thread_exiting_event(false, false); + scoped_ptr<ChannelHookup<SyncerEvent> > hookup; + hookup.reset(syncer_thread()->relay_channel()->AddObserver(&listener)); + + EXPECT_CALL(listener, HandleChannelEvent( + Field(&SyncerEvent::what_happened, SyncerEvent::STATUS_CHANGED))). + Times(AnyNumber()); + + EXPECT_CALL(listener, HandleChannelEvent( + Field(&SyncerEvent::what_happened, SyncerEvent::SYNC_CYCLE_ENDED))). + Times(AnyNumber()). + WillOnce(SignalEvent(&sync_cycle_ended_event)); + + EXPECT_CALL(listener, HandleChannelEvent( + Field(&SyncerEvent::what_happened, + SyncerEvent::STOP_SYNCING_PERMANENTLY))); + EXPECT_CALL(listener, HandleChannelEvent( + Field(&SyncerEvent::what_happened, SyncerEvent::SYNCER_THREAD_EXITING))). + WillOnce(SignalEvent(&syncer_thread_exiting_event)); + + EXPECT_TRUE(syncer_thread()->Start()); + metadb()->Open(); + ASSERT_TRUE(sync_cycle_ended_event.TimedWait(max_wait_time_)); + + connection()->set_store_birthday("NotYourLuckyDay"); + ASSERT_TRUE(syncer_thread_exiting_event.TimedWait(max_wait_time_)); + EXPECT_TRUE(syncer_thread()->Stop(0)); +} + TEST_F(SyncerThreadWithSyncerTest, AuthInvalid) { SyncShareIntercept interceptor; connection()->SetMidCommitObserver(&interceptor); @@ -789,6 +826,8 @@ TEST_F(SyncerThreadWithSyncerTest, MAYBE_Pause) { EXPECT_CALL(listener, HandleChannelEvent( Field(&SyncerEvent::what_happened, SyncerEvent::SYNC_CYCLE_ENDED))). WillOnce(SignalEvent(&sync_cycle_ended_event)); + EXPECT_CALL(listener, HandleChannelEvent( + Field(&SyncerEvent::what_happened, SyncerEvent::SYNCER_THREAD_EXITING))); ASSERT_TRUE(syncer_thread()->Start()); metadb()->Open(); ASSERT_TRUE(sync_cycle_ended_event.TimedWait(max_wait_time_)); @@ -829,6 +868,8 @@ TEST_F(SyncerThreadWithSyncerTest, StartWhenNotConnected) { EXPECT_CALL(listener, HandleChannelEvent( Field(&SyncerEvent::what_happened, SyncerEvent::STATUS_CHANGED))). Times(AnyNumber()); + EXPECT_CALL(listener, HandleChannelEvent( + Field(&SyncerEvent::what_happened, SyncerEvent::SYNCER_THREAD_EXITING))); connection()->SetServerNotReachable(); metadb()->Open(); @@ -928,6 +969,9 @@ TEST_F(SyncerThreadWithSyncerTest, FLAKY_PauseWhenNotConnected) { EXPECT_CALL(listener, HandleChannelEvent( Field(&SyncerEvent::what_happened, SyncerEvent::SYNC_CYCLE_ENDED))). WillOnce(SignalEvent(&sync_cycle_ended_event)); + EXPECT_CALL(listener, HandleChannelEvent( + Field(&SyncerEvent::what_happened, SyncerEvent::SYNCER_THREAD_EXITING))); + syncer_thread()->NudgeSyncer(0, SyncerThread::kUnknown); ASSERT_TRUE(sync_cycle_ended_event.TimedWait(max_wait_time_)); @@ -959,9 +1003,11 @@ TEST_F(SyncerThreadWithSyncerTest, PauseResumeWhenNotRunning) { EXPECT_CALL(listener, HandleChannelEvent( Field(&SyncerEvent::what_happened, SyncerEvent::SYNC_CYCLE_ENDED))). WillOnce(SignalEvent(&sync_cycle_ended_event)); + EXPECT_CALL(listener, HandleChannelEvent( + Field(&SyncerEvent::what_happened, SyncerEvent::SYNCER_THREAD_EXITING))); + ASSERT_TRUE(Resume(&listener)); ASSERT_TRUE(sync_cycle_ended_event.TimedWait(max_wait_time_)); - EXPECT_TRUE(syncer_thread()->Stop(2000)); } diff --git a/chrome/browser/sync/engine/syncer_types.h b/chrome/browser/sync/engine/syncer_types.h index 5a3f68a..734f823 100644 --- a/chrome/browser/sync/engine/syncer_types.h +++ b/chrome/browser/sync/engine/syncer_types.h @@ -107,6 +107,15 @@ struct SyncerEvent { // This event is sent when a connection has been established and // the thread continues. CONNECTED, + + // This is sent after the Syncer (and SyncerThread) have initiated self + // halt due to no longer being permitted to communicate with the server. + // The listener should sever the sync / browser connections and delete sync + // data (i.e. as if the user clicked 'Stop Syncing' in the browser. + STOP_SYNCING_PERMANENTLY, + + // Sent when the main syncer loop finishes. + SYNCER_THREAD_EXITING, }; explicit SyncerEvent(EventCause cause) : what_happened(cause), diff --git a/chrome/browser/sync/engine/syncer_unittest.cc b/chrome/browser/sync/engine/syncer_unittest.cc index f0a8cb0..f2077e3 100644 --- a/chrome/browser/sync/engine/syncer_unittest.cc +++ b/chrome/browser/sync/engine/syncer_unittest.cc @@ -115,6 +115,8 @@ class SyncerTest : public testing::Test, const base::TimeDelta& new_interval) { last_short_poll_interval_received_ = new_interval; } + virtual void OnShouldStopSyncingPermanently() { + } // ModelSafeWorkerRegistrar implementation. virtual void GetWorkers(std::vector<ModelSafeWorker*>* out) { diff --git a/chrome/browser/sync/glue/sync_backend_host.cc b/chrome/browser/sync/glue/sync_backend_host.cc index 15d78cf..73fee4d 100644 --- a/chrome/browser/sync/glue/sync_backend_host.cc +++ b/chrome/browser/sync/glue/sync_backend_host.cc @@ -592,6 +592,15 @@ void SyncBackendHost::Core::OnResumed() { NewRunnableMethod(this, &Core::NotifyResumed)); } +void SyncBackendHost::Core::OnStopSyncingPermanently() { + host_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, + &Core::HandleStopSyncingPermanentlyOnFrontendLoop)); +} + +void SyncBackendHost::Core::HandleStopSyncingPermanentlyOnFrontendLoop() { + host_->frontend_->OnStopSyncingPermanently(); +} + void SyncBackendHost::Core::HandleAuthErrorEventOnFrontendLoop( const GoogleServiceAuthError& new_auth_error) { if (!host_ || !host_->frontend_) diff --git a/chrome/browser/sync/glue/sync_backend_host.h b/chrome/browser/sync/glue/sync_backend_host.h index a91d877..41a388a 100644 --- a/chrome/browser/sync/glue/sync_backend_host.h +++ b/chrome/browser/sync/glue/sync_backend_host.h @@ -58,6 +58,10 @@ class SyncFrontend { // credentials to be provided. See SyncBackendHost::Authenticate for details. virtual void OnAuthError() = 0; + // We are no longer permitted to communicate with the server. Sync should + // be disabled and state cleaned up at once. + virtual void OnStopSyncingPermanently() = 0; + protected: // Don't delete through SyncFrontend interface. virtual ~SyncFrontend() { @@ -230,6 +234,7 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar { virtual void OnPassphraseAccepted(); virtual void OnPaused(); virtual void OnResumed(); + virtual void OnStopSyncingPermanently(); struct DoInitializeOptions { DoInitializeOptions( @@ -374,6 +379,8 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar { void HandleSyncCycleCompletedOnFrontendLoop( sessions::SyncSessionSnapshot* snapshot); + void HandleStopSyncingPermanentlyOnFrontendLoop(); + // Called from Core::OnInitializationComplete to handle updating // frontend thread components. void HandleInitalizationCompletedOnFrontendLoop(); diff --git a/chrome/browser/sync/profile_sync_service.cc b/chrome/browser/sync/profile_sync_service.cc index 9695b84..055f039 100644 --- a/chrome/browser/sync/profile_sync_service.cc +++ b/chrome/browser/sync/profile_sync_service.cc @@ -322,11 +322,6 @@ void ProfileSyncService::EnableForUser() { } void ProfileSyncService::DisableForUser() { - if (WizardIsVisible()) { - // TODO(timsteele): Focus wizard. - return; - } - LOG(INFO) << "Clearing Sync DB."; // Clear prefs (including SyncSetupHasCompleted) before shutting down so @@ -403,7 +398,7 @@ void ProfileSyncService::OnUnrecoverableError( from_here.Write(true, true, &location); LOG(ERROR) << location; - if (WizardIsVisible()) { + if (SetupInProgress()) { // We've hit an error in the middle of a startup process- shutdown all the // backend stuff, and then restart it, so we're in the same state as before. MessageLoop::current()->PostTask(FROM_HERE, @@ -468,6 +463,13 @@ void ProfileSyncService::OnAuthError() { FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged()); } +void ProfileSyncService::OnStopSyncingPermanently() { + if (SetupInProgress()) + wizard_.Step(SyncSetupWizard::FATAL_ERROR); + + DisableForUser(); +} + void ProfileSyncService::ShowLoginDialog() { if (WizardIsVisible()) { wizard_.Focus(); diff --git a/chrome/browser/sync/profile_sync_service.h b/chrome/browser/sync/profile_sync_service.h index b995ffa..5b36e0b 100644 --- a/chrome/browser/sync/profile_sync_service.h +++ b/chrome/browser/sync/profile_sync_service.h @@ -156,6 +156,7 @@ class ProfileSyncService : public browser_sync::SyncFrontend, virtual void OnBackendInitialized(); virtual void OnSyncCycleCompleted(); virtual void OnAuthError(); + virtual void OnStopSyncingPermanently(); // Called when a user enters credentials through UI. virtual void OnUserSubmittedAuth(const std::string& username, diff --git a/chrome/browser/sync/sessions/sync_session.h b/chrome/browser/sync/sessions/sync_session.h index 6f29227..dd498fe 100644 --- a/chrome/browser/sync/sessions/sync_session.h +++ b/chrome/browser/sync/sessions/sync_session.h @@ -65,6 +65,12 @@ class SyncSession { virtual void OnReceivedLongPollIntervalUpdate( const base::TimeDelta& new_interval) = 0; + // The client needs to cease and desist syncing at once. This occurs when + // the Syncer detects that the backend store has fundamentally changed or + // is a different instance altogether (e.g. swapping from a test instance + // to production, or a global stop syncing operation has wiped the store). + virtual void OnShouldStopSyncingPermanently() = 0; + protected: virtual ~Delegate() {} }; diff --git a/chrome/browser/sync/sessions/sync_session_unittest.cc b/chrome/browser/sync/sessions/sync_session_unittest.cc index ae85137..2bc4594 100644 --- a/chrome/browser/sync/sessions/sync_session_unittest.cc +++ b/chrome/browser/sync/sessions/sync_session_unittest.cc @@ -50,6 +50,9 @@ class SyncSessionTest : public testing::Test, const base::TimeDelta& new_interval) { FailControllerInvocationIfDisabled("OnReceivedShortPollIntervalUpdate"); } + virtual void OnShouldStopSyncingPermanently() { + FailControllerInvocationIfDisabled("OnShouldStopSyncingPermanently"); + } // ModelSafeWorkerRegistrar implementation. virtual void GetWorkers(std::vector<ModelSafeWorker*>* out) {} diff --git a/chrome/test/sync/engine/mock_connection_manager.h b/chrome/test/sync/engine/mock_connection_manager.h index be2144f..dd2573d 100644 --- a/chrome/test/sync/engine/mock_connection_manager.h +++ b/chrome/test/sync/engine/mock_connection_manager.h @@ -177,6 +177,10 @@ class MockConnectionManager : public browser_sync::ServerConnectionManager { use_legacy_bookmarks_protocol_ = value; } + void set_store_birthday(string new_birthday) { + store_birthday_ = new_birthday; + } + // Retrieve the number of GetUpdates requests that the mock server has // seen since the last time this function was called. Can be used to // verify that a GetUpdates actually did or did not happen after running diff --git a/chrome/test/sync/engine/syncer_command_test.h b/chrome/test/sync/engine/syncer_command_test.h index 9a3c87c..719a22d 100644 --- a/chrome/test/sync/engine/syncer_command_test.h +++ b/chrome/test/sync/engine/syncer_command_test.h @@ -44,6 +44,9 @@ class SyncerCommandTestWithParam : public testing::TestWithParam<T>, const base::TimeDelta& new_interval) { FAIL() << "Should not get poll interval update."; } + virtual void OnShouldStopSyncingPermanently() { + FAIL() << "Shouldn't be forced to stop syncing."; + } // ModelSafeWorkerRegistrar implementation. virtual void GetWorkers(std::vector<ModelSafeWorker*>* out) { |