summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorskrul@chromium.org <skrul@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-15 21:00:35 +0000
committerskrul@chromium.org <skrul@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-03-15 21:00:35 +0000
commitf5bf3a3dc886d662b94e0705968a2e9ebd020033 (patch)
tree0a3614879f6b4f398c505ccd6aeda6e5e09c4b3c
parent74215679409db25951507832685f76080650ca6e (diff)
downloadchromium_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.cc6
-rw-r--r--chrome/browser/sync/engine/syncer_thread.cc45
-rw-r--r--chrome/browser/sync/engine/syncer_thread.h26
-rw-r--r--chrome/browser/sync/engine/syncer_thread_unittest.cc68
-rw-r--r--chrome/browser/sync/engine/syncer_types.h8
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),