diff options
author | skrul@chromium.org <skrul@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-15 21:00:35 +0000 |
---|---|---|
committer | skrul@chromium.org <skrul@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-15 21:00:35 +0000 |
commit | f5bf3a3dc886d662b94e0705968a2e9ebd020033 (patch) | |
tree | 0a3614879f6b4f398c505ccd6aeda6e5e09c4b3c | |
parent | 74215679409db25951507832685f76080650ca6e (diff) | |
download | chromium_src-f5bf3a3dc886d662b94e0705968a2e9ebd020033.zip chromium_src-f5bf3a3dc886d662b94e0705968a2e9ebd020033.tar.gz chromium_src-f5bf3a3dc886d662b94e0705968a2e9ebd020033.tar.bz2 |
Implement SyncerThread::RequestPause and SyncerThread::RequestResume
Basic functionality with unit test. More plumbing later :)
Review URL: http://codereview.chromium.org/847004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@41628 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/sync/engine/all_status.cc | 6 | ||||
-rw-r--r-- | chrome/browser/sync/engine/syncer_thread.cc | 45 | ||||
-rw-r--r-- | chrome/browser/sync/engine/syncer_thread.h | 26 | ||||
-rw-r--r-- | chrome/browser/sync/engine/syncer_thread_unittest.cc | 68 | ||||
-rw-r--r-- | chrome/browser/sync/engine/syncer_types.h | 8 |
5 files changed, 151 insertions, 2 deletions
diff --git a/chrome/browser/sync/engine/all_status.cc b/chrome/browser/sync/engine/all_status.cc index 1a7fa78..da3a3f0 100644 --- a/chrome/browser/sync/engine/all_status.cc +++ b/chrome/browser/sync/engine/all_status.cc @@ -235,6 +235,12 @@ void AllStatus::HandleSyncerEvent(const SyncerEvent& event) { 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; default: LOG(ERROR) << "Unrecognized Syncer Event: " << event.what_happened; lock.set_notify_plan(DONT_NOTIFY); diff --git a/chrome/browser/sync/engine/syncer_thread.cc b/chrome/browser/sync/engine/syncer_thread.cc index b89133d..33354d0 100644 --- a/chrome/browser/sync/engine/syncer_thread.cc +++ b/chrome/browser/sync/engine/syncer_thread.cc @@ -162,6 +162,26 @@ bool SyncerThread::Stop(int max_wait) { return true; } +bool SyncerThread::RequestPause() { + AutoLock lock(lock_); + if (!thread_.IsRunning()) + return false; + + vault_.pause_ = true; + vault_field_changed_.Broadcast(); + return true; +} + +bool SyncerThread::RequestResume() { + AutoLock lock(lock_); + if (!thread_.IsRunning() || !vault_.pause_) + return false; + + vault_.pause_ = false; + vault_field_changed_.Broadcast(); + return true; +} + void SyncerThread::OnReceivedLongPollIntervalUpdate( const base::TimeDelta& new_interval) { syncer_long_poll_interval_seconds_ = static_cast<int>( @@ -220,6 +240,12 @@ void SyncerThread::ThreadMainLoop() { continue; } + // Check if a pause was requested. + if (vault_.pause_) { + PauseUntilResumedOrQuit(); + continue; + } + const TimeTicks next_poll = last_sync_time + vault_.current_wait_interval_.poll_delta; bool throttled = vault_.current_wait_interval_.mode == @@ -273,6 +299,24 @@ void SyncerThread::ThreadMainLoop() { #endif } +void SyncerThread::PauseUntilResumedOrQuit() { + LOG(INFO) << "Syncer thread entering pause."; + SyncerEvent event(SyncerEvent::PAUSED); + relay_channel()->NotifyListeners(event); + + // Thread will get stuck here until either a resume is requested + // or shutdown is started. + while (vault_.pause_ && !vault_.stop_syncer_thread_) + vault_field_changed_.Wait(); + + // Notify that we have resumed if we are not shutting down. + if (!vault_.stop_syncer_thread_) { + SyncerEvent event(SyncerEvent::RESUMED); + relay_channel()->NotifyListeners(event); + } + LOG(INFO) << "Syncer thread exiting pause."; +} + // We check how long the user's been idle and sync less often if the machine is // not in use. The aim is to reduce server load. // TODO(timsteele): Should use Time(Delta). @@ -669,5 +713,4 @@ int SyncerThread::UserIdleTime() { return 0; } - } // namespace browser_sync diff --git a/chrome/browser/sync/engine/syncer_thread.h b/chrome/browser/sync/engine/syncer_thread.h index 5d06bc2..71cbcbe 100644 --- a/chrome/browser/sync/engine/syncer_thread.h +++ b/chrome/browser/sync/engine/syncer_thread.h @@ -55,6 +55,7 @@ class SyncerThread : public base::RefCountedThreadSafe<SyncerThread>, FRIEND_TEST(SyncerThreadWithSyncerTest, Nudge); FRIEND_TEST(SyncerThreadWithSyncerTest, Throttling); FRIEND_TEST(SyncerThreadWithSyncerTest, AuthInvalid); + FRIEND_TEST(SyncerThreadWithSyncerTest, PauseWhileWaiting); friend class SyncerThreadWithSyncerTest; friend class SyncerThreadFactory; public: @@ -113,6 +114,19 @@ class SyncerThread : public base::RefCountedThreadSafe<SyncerThread>, // Stop processing. |max_wait| doesn't do anything in this version. virtual bool Stop(int max_wait); + // Request that the thread pauses. Returns false if the request can + // not be completed (e.g. the thread is not running). When the + // thread actually pauses, a SyncerEvent::PAUSED event notification + // will be sent to the relay channel. + virtual bool RequestPause(); + + // Request that the thread resumes from pause. Returns false if the + // request can not be completed (e.g. the thread is not running or + // is not currently paused). When the thread actually resumes, a + // SyncerEvent::RESUMED event notification will be sent to the relay + // channel. + virtual bool RequestResume(); + // Nudges the syncer to sync with a delay specified. This API is for access // from the SyncerThread's controller and will cause a mutex lock. virtual void NudgeSyncer(int milliseconds_from_now, NudgeSource source); @@ -168,6 +182,9 @@ class SyncerThread : public base::RefCountedThreadSafe<SyncerThread>, // False when we want to stop the thread. bool stop_syncer_thread_; + // True when the thread should pause itself. + bool pause_; + Syncer* syncer_; // State of the server connection. @@ -185,7 +202,10 @@ class SyncerThread : public base::RefCountedThreadSafe<SyncerThread>, WaitInterval current_wait_interval_; ProtectedFields() - : stop_syncer_thread_(false), syncer_(NULL), connected_(false) {} + : stop_syncer_thread_(false), + pause_(false), + syncer_(NULL), + connected_(false) {} } vault_; // Gets signaled whenever a thread outside of the syncer thread changes a @@ -253,6 +273,10 @@ class SyncerThread : public base::RefCountedThreadSafe<SyncerThread>, int UserIdleTime(); + // The thread will remain in this method until a resume is requested + // or shutdown is started. + void PauseUntilResumedOrQuit(); + // For unit tests only. virtual void DisableIdleDetection() { disable_idle_detection_ = true; } diff --git a/chrome/browser/sync/engine/syncer_thread_unittest.cc b/chrome/browser/sync/engine/syncer_thread_unittest.cc index 2f9c9b6..be6be6a 100644 --- a/chrome/browser/sync/engine/syncer_thread_unittest.cc +++ b/chrome/browser/sync/engine/syncer_thread_unittest.cc @@ -11,13 +11,18 @@ #include "base/waitable_event.h" #include "chrome/browser/sync/engine/model_safe_worker.h" #include "chrome/browser/sync/engine/syncer_thread.h" +#include "chrome/browser/sync/engine/syncer_types.h" #include "chrome/browser/sync/sessions/sync_session_context.h" #include "chrome/test/sync/engine/mock_server_connection.h" #include "chrome/test/sync/engine/test_directory_setter_upper.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" using base::TimeTicks; using base::TimeDelta; +using base::WaitableEvent; +using testing::_; +using testing::Field; namespace browser_sync { using sessions::SyncSessionContext; @@ -705,4 +710,67 @@ TEST_F(SyncerThreadWithSyncerTest, AuthInvalid) { EXPECT_TRUE(syncer_thread()->Stop(2000)); } +ACTION_P(SignalEvent, event) { + event->Signal(); +} + +class ListenerMock { + public: + MOCK_METHOD1(HandleEvent, void(const SyncerEvent&)); +}; + +TEST_F(SyncerThreadWithSyncerTest, PauseWhileWaiting) { + WaitableEvent event(false, false); + SyncShareIntercept interceptor; + connection()->SetMidCommitObserver(&interceptor); + // We don't want a poll to happen during this test (except the first one). + const TimeDelta poll_interval = TimeDelta::FromMinutes(5); + syncer_thread()->SetSyncerShortPollInterval(poll_interval); + + ListenerMock listener; + scoped_ptr<EventListenerHookup> hookup; + hookup.reset( + NewEventListenerHookup(syncer_thread()->relay_channel(), + &listener, + &ListenerMock::HandleEvent)); + + EXPECT_CALL(listener, HandleEvent( + Field(&SyncerEvent::what_happened, SyncerEvent::STATUS_CHANGED))). + Times(2); + EXPECT_CALL(listener, HandleEvent( + Field(&SyncerEvent::what_happened, SyncerEvent::SYNC_CYCLE_ENDED))); + + EXPECT_TRUE(syncer_thread()->Start()); + metadb()->Open(); + + // Wait for thread to be waiting. + interceptor.WaitForSyncShare(1, poll_interval + poll_interval); + EXPECT_EQ(1U, interceptor.times_sync_occured().size()); + + // Request a pause. + EXPECT_CALL(listener, HandleEvent( + Field(&SyncerEvent::what_happened, SyncerEvent::PAUSED))). + WillOnce(SignalEvent(&event)); + ASSERT_TRUE(syncer_thread()->RequestPause()); + event.Wait(); + + // Nudge the syncer, this should do nothing while we are paused. If + // it does, the next test of "times sync occured" will fail. + syncer_thread()->NudgeSyncer(0, SyncerThread::kUnknown); + + // Resuming will cause the thread to wait again without running the + // syncer. + EXPECT_CALL(listener, HandleEvent( + Field(&SyncerEvent::what_happened, SyncerEvent::RESUMED))). + WillOnce(SignalEvent(&event)); + ASSERT_TRUE(syncer_thread()->RequestResume()); + event.Wait(); + + // Confirm the syncer ran immediately after resume. + interceptor.WaitForSyncShare(1, TimeDelta::FromSeconds(1)); + EXPECT_EQ(2U, interceptor.times_sync_occured().size()); + + EXPECT_TRUE(syncer_thread()->Stop(2000)); +} + } // namespace browser_sync diff --git a/chrome/browser/sync/engine/syncer_types.h b/chrome/browser/sync/engine/syncer_types.h index 79c53f9..b506383 100644 --- a/chrome/browser/sync/engine/syncer_types.h +++ b/chrome/browser/sync/engine/syncer_types.h @@ -91,6 +91,14 @@ struct SyncerEvent { // Check the SyncerSession for information like whether we need to continue // syncing (SyncerSession::HasMoreToSync). SYNC_CYCLE_ENDED, + + // This event is sent when the thread is paused in response to a + // pause request. + PAUSED, + + // This event is sent when the thread is resumed in response to a + // resume request. + RESUMED, }; explicit SyncerEvent(EventCause cause) : what_happened(cause), |