diff options
author | akalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-06-14 09:51:46 +0000 |
---|---|---|
committer | akalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-06-14 09:51:46 +0000 |
commit | 2aa4ec16d252d37c12fa6d8a187350a906808a7a (patch) | |
tree | 74310906434c1854629fa377e4ee74f247f3cf0e | |
parent | 1ba346dd63e728e508be1e84aa234c53c7d6e1d3 (diff) | |
download | chromium_src-2aa4ec16d252d37c12fa6d8a187350a906808a7a.zip chromium_src-2aa4ec16d252d37c12fa6d8a187350a906808a7a.tar.gz chromium_src-2aa4ec16d252d37c12fa6d8a187350a906808a7a.tar.bz2 |
[Sync] Combine sync thread and sync core thread.
Rename SyncerThread to SyncerScheduler and its owned thread; it is now
a (mostly) non-thread-safe class which schedules events on the message
loop it was created on.
Rename core_thread_ in SyncBackendHost to sync_thread_ (and associated
variables).
BUG=79174
TEST=
Review URL: http://codereview.chromium.org/6995097
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@88976 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/sync/engine/sync_scheduler.cc (renamed from chrome/browser/sync/engine/syncer_thread.cc) | 329 | ||||
-rw-r--r-- | chrome/browser/sync/engine/sync_scheduler.h (renamed from chrome/browser/sync/engine/syncer_thread.h) | 105 | ||||
-rw-r--r-- | chrome/browser/sync/engine/sync_scheduler_unittest.cc (renamed from chrome/browser/sync/engine/syncer_thread_unittest.cc) | 773 | ||||
-rw-r--r-- | chrome/browser/sync/engine/sync_scheduler_whitebox_unittest.cc | 233 | ||||
-rw-r--r-- | chrome/browser/sync/engine/syncapi.cc | 174 | ||||
-rw-r--r-- | chrome/browser/sync/engine/syncapi.h | 11 | ||||
-rw-r--r-- | chrome/browser/sync/engine/syncer_thread_whitebox_unittest.cc | 232 | ||||
-rw-r--r-- | chrome/browser/sync/glue/sync_backend_host.cc | 75 | ||||
-rw-r--r-- | chrome/browser/sync/glue/sync_backend_host.h | 53 | ||||
-rw-r--r-- | chrome/browser/sync/test_profile_sync_service.cc | 2 | ||||
-rw-r--r-- | chrome/chrome.gyp | 4 | ||||
-rw-r--r-- | chrome/chrome_tests.gypi | 4 |
12 files changed, 1084 insertions, 911 deletions
diff --git a/chrome/browser/sync/engine/syncer_thread.cc b/chrome/browser/sync/engine/sync_scheduler.cc index 44c528c..22fd7f6 100644 --- a/chrome/browser/sync/engine/syncer_thread.cc +++ b/chrome/browser/sync/engine/sync_scheduler.cc @@ -2,10 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/sync/engine/syncer_thread.h" +#include "chrome/browser/sync/engine/sync_scheduler.h" #include <algorithm> +#include "base/compiler_specific.h" +#include "base/message_loop.h" #include "base/rand_util.h" #include "chrome/browser/sync/engine/syncer.h" @@ -21,16 +23,16 @@ using syncable::ModelTypePayloadMap; using syncable::ModelTypeBitSet; using sync_pb::GetUpdatesCallerInfo; -SyncerThread::DelayProvider::DelayProvider() {} -SyncerThread::DelayProvider::~DelayProvider() {} +SyncScheduler::DelayProvider::DelayProvider() {} +SyncScheduler::DelayProvider::~DelayProvider() {} -SyncerThread::WaitInterval::WaitInterval() {} -SyncerThread::WaitInterval::~WaitInterval() {} +SyncScheduler::WaitInterval::WaitInterval() {} +SyncScheduler::WaitInterval::~WaitInterval() {} -SyncerThread::SyncSessionJob::SyncSessionJob() {} -SyncerThread::SyncSessionJob::~SyncSessionJob() {} +SyncScheduler::SyncSessionJob::SyncSessionJob() {} +SyncScheduler::SyncSessionJob::~SyncSessionJob() {} -SyncerThread::SyncSessionJob::SyncSessionJob(SyncSessionJobPurpose purpose, +SyncScheduler::SyncSessionJob::SyncSessionJob(SyncSessionJobPurpose purpose, base::TimeTicks start, linked_ptr<sessions::SyncSession> session, bool is_canary_job, const tracked_objects::Location& nudge_location) : purpose(purpose), @@ -40,9 +42,9 @@ SyncerThread::SyncSessionJob::SyncSessionJob(SyncSessionJobPurpose purpose, nudge_location(nudge_location) { } -TimeDelta SyncerThread::DelayProvider::GetDelay( +TimeDelta SyncScheduler::DelayProvider::GetDelay( const base::TimeDelta& last_delay) { - return SyncerThread::GetRecommendedDelay(last_delay); + return SyncScheduler::GetRecommendedDelay(last_delay); } GetUpdatesCallerInfo::GetUpdatesSource GetUpdatesFromNudgeSource( @@ -76,18 +78,20 @@ GetUpdatesCallerInfo::GetUpdatesSource GetSourceFromReason( return GetUpdatesCallerInfo::UNKNOWN; } -SyncerThread::WaitInterval::WaitInterval(Mode mode, TimeDelta length) +SyncScheduler::WaitInterval::WaitInterval(Mode mode, TimeDelta length) : mode(mode), had_nudge(false), length(length) { } // Helper macro to log with the syncer thread name; useful when there // are multiple syncer threads involved. #define SVLOG(verbose_level) VLOG(verbose_level) << name_ << ": " -SyncerThread::SyncerThread(const std::string& name, - sessions::SyncSessionContext* context, - Syncer* syncer) - : name_(name), - thread_("SyncEngine_SyncerThread"), +SyncScheduler::SyncScheduler(const std::string& name, + sessions::SyncSessionContext* context, + Syncer* syncer) + : method_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), + name_(name), + sync_loop_(MessageLoop::current()), + started_(false), syncer_short_poll_interval_seconds_( TimeDelta::FromSeconds(kDefaultShortPollIntervalSeconds)), syncer_long_poll_interval_seconds_( @@ -97,17 +101,20 @@ SyncerThread::SyncerThread(const std::string& name, delay_provider_(new DelayProvider()), syncer_(syncer), session_context_(context) { + DCHECK(sync_loop_); } -SyncerThread::~SyncerThread() { - DCHECK(!thread_.IsRunning()); +SyncScheduler::~SyncScheduler() { + DCHECK_EQ(MessageLoop::current(), sync_loop_); + Stop(); } -void SyncerThread::CheckServerConnectionManagerStatus( +void SyncScheduler::CheckServerConnectionManagerStatus( HttpResponse::ServerConnectionCode code) { + DCHECK_EQ(MessageLoop::current(), sync_loop_); bool old_server_connection_ok = server_connection_ok_; - // Note, be careful when adding cases here because if the SyncerThread + // Note, be careful when adding cases here because if the SyncScheduler // thinks there is no valid connection as determined by this method, it // will drop out of *all* forward progress sync loops (it won't poll and it // will queue up Talk notifications but not actually call SyncShare) until @@ -128,27 +135,29 @@ void SyncerThread::CheckServerConnectionManagerStatus( } } -void SyncerThread::Start(Mode mode, ModeChangeCallback* callback) { +void SyncScheduler::Start(Mode mode, ModeChangeCallback* callback) { + DCHECK_EQ(MessageLoop::current(), sync_loop_); SVLOG(2) << "Start called from thread " << MessageLoop::current()->thread_name() << " with mode " << mode; - if (!thread_.IsRunning()) { - SVLOG(2) << "Starting thread with mode " << mode; - if (!thread_.Start()) { - NOTREACHED() << "Unable to start SyncerThread."; - return; - } + if (!started_) { WatchConnectionManager(); - thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod( - this, &SyncerThread::SendInitialSnapshot)); + sync_loop_->PostTask( + FROM_HERE, + method_factory_.NewRunnableMethod( + &SyncScheduler::SendInitialSnapshot)); } - - thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod( - this, &SyncerThread::StartImpl, mode, callback)); + started_ = true; + // TODO(sync): This will leak if StartImpl is never run. Fix this. + // Might be easiest to just use base::Callback. + sync_loop_->PostTask( + FROM_HERE, + method_factory_.NewRunnableMethod( + &SyncScheduler::StartImpl, mode, callback)); } -void SyncerThread::SendInitialSnapshot() { - DCHECK_EQ(MessageLoop::current(), thread_.message_loop()); +void SyncScheduler::SendInitialSnapshot() { + DCHECK_EQ(MessageLoop::current(), sync_loop_); scoped_ptr<SyncSession> dummy(new SyncSession(session_context_.get(), this, SyncSourceInfo(), ModelSafeRoutingInfo(), std::vector<ModelSafeWorker*>())); @@ -158,19 +167,23 @@ void SyncerThread::SendInitialSnapshot() { session_context_->NotifyListeners(event); } -void SyncerThread::WatchConnectionManager() { +void SyncScheduler::WatchConnectionManager() { + DCHECK_EQ(MessageLoop::current(), sync_loop_); ServerConnectionManager* scm = session_context_->connection_manager(); - CheckServerConnectionManagerStatus(scm->server_status()); + sync_loop_->PostTask( + FROM_HERE, + method_factory_.NewRunnableMethod( + &SyncScheduler::CheckServerConnectionManagerStatus, + scm->server_status())); scm->AddListener(this); } -void SyncerThread::StartImpl(Mode mode, ModeChangeCallback* callback) { +void SyncScheduler::StartImpl(Mode mode, ModeChangeCallback* callback) { + DCHECK_EQ(MessageLoop::current(), sync_loop_); SVLOG(2) << "Doing StartImpl with mode " << mode; - // TODO(lipalani): This will leak if startimpl is never run. Fix it using a - // ThreadSafeRefcounted object. scoped_ptr<ModeChangeCallback> scoped_callback(callback); - DCHECK_EQ(MessageLoop::current(), thread_.message_loop()); + DCHECK_EQ(MessageLoop::current(), sync_loop_); DCHECK(!session_context_->account_name().empty()); DCHECK(syncer_.get()); mode_ = mode; @@ -183,9 +196,9 @@ void SyncerThread::StartImpl(Mode mode, ModeChangeCallback* callback) { DoPendingJobIfPossible(false); } -SyncerThread::JobProcessDecision SyncerThread::DecideWhileInWaitInterval( +SyncScheduler::JobProcessDecision SyncScheduler::DecideWhileInWaitInterval( const SyncSessionJob& job) { - + DCHECK_EQ(MessageLoop::current(), sync_loop_); DCHECK(wait_interval_.get()); DCHECK_NE(job.purpose, SyncSessionJob::CLEAR_USER_DATA); @@ -215,8 +228,9 @@ SyncerThread::JobProcessDecision SyncerThread::DecideWhileInWaitInterval( return job.is_canary_job ? CONTINUE : SAVE; } -SyncerThread::JobProcessDecision SyncerThread::DecideOnJob( +SyncScheduler::JobProcessDecision SyncScheduler::DecideOnJob( const SyncSessionJob& job) { + DCHECK_EQ(MessageLoop::current(), sync_loop_); if (job.purpose == SyncSessionJob::CLEAR_USER_DATA) return CONTINUE; @@ -249,7 +263,8 @@ SyncerThread::JobProcessDecision SyncerThread::DecideOnJob( return job.purpose == SyncSessionJob::NUDGE ? SAVE : DROP; } -void SyncerThread::InitOrCoalescePendingJob(const SyncSessionJob& job) { +void SyncScheduler::InitOrCoalescePendingJob(const SyncSessionJob& job) { + DCHECK_EQ(MessageLoop::current(), sync_loop_); DCHECK(job.purpose != SyncSessionJob::CONFIGURATION); if (pending_nudge_.get() == NULL) { SVLOG(2) << "Creating a pending nudge job"; @@ -272,7 +287,8 @@ void SyncerThread::InitOrCoalescePendingJob(const SyncSessionJob& job) { // location of the first caller. } -bool SyncerThread::ShouldRunJob(const SyncSessionJob& job) { +bool SyncScheduler::ShouldRunJob(const SyncSessionJob& job) { + DCHECK_EQ(MessageLoop::current(), sync_loop_); JobProcessDecision decision = DecideOnJob(job); SVLOG(2) << "Should run job, decision: " << decision << ", job purpose " << job.purpose @@ -287,7 +303,8 @@ bool SyncerThread::ShouldRunJob(const SyncSessionJob& job) { return false; } -void SyncerThread::SaveJob(const SyncSessionJob& job) { +void SyncScheduler::SaveJob(const SyncSessionJob& job) { + DCHECK_EQ(MessageLoop::current(), sync_loop_); DCHECK(job.purpose != SyncSessionJob::CLEAR_USER_DATA); if (job.purpose == SyncSessionJob::NUDGE) { SVLOG(2) << "Saving a nudge job"; @@ -301,7 +318,7 @@ void SyncerThread::SaveJob(const SyncSessionJob& job) { SyncSession* s(new SyncSession(session_context_.get(), this, old->source(), old->routing_info(), old->workers())); SyncSessionJob new_job(job.purpose, TimeTicks::Now(), - make_linked_ptr(s), false, job.nudge_location); + make_linked_ptr(s), false, job.nudge_location); wait_interval_->pending_configure_job.reset(new SyncSessionJob(new_job)); } // drop the rest. } @@ -315,52 +332,46 @@ struct ModelSafeWorkerGroupIs { ModelSafeGroup group; }; -void SyncerThread::ScheduleClearUserData() { - if (!thread_.IsRunning()) { - NOTREACHED(); - return; - } - thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod( - this, &SyncerThread::ScheduleClearUserDataImpl)); +void SyncScheduler::ScheduleClearUserData() { + DCHECK_EQ(MessageLoop::current(), sync_loop_); + sync_loop_->PostTask( + FROM_HERE, + method_factory_.NewRunnableMethod( + &SyncScheduler::ScheduleClearUserDataImpl)); } -void SyncerThread::ScheduleNudge(const TimeDelta& delay, +void SyncScheduler::ScheduleNudge(const TimeDelta& delay, NudgeSource source, const ModelTypeBitSet& types, const tracked_objects::Location& nudge_location) { - if (!thread_.IsRunning()) { - LOG(INFO) << "Dropping nudge because thread is not running."; - NOTREACHED(); - return; - } - + DCHECK_EQ(MessageLoop::current(), sync_loop_); SVLOG(2) << "Nudge scheduled (source=" << source << ")"; ModelTypePayloadMap types_with_payloads = syncable::ModelTypePayloadMapFromBitSet(types, std::string()); - thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod( - this, &SyncerThread::ScheduleNudgeImpl, delay, - GetUpdatesFromNudgeSource(source), types_with_payloads, false, - nudge_location)); + sync_loop_->PostTask( + FROM_HERE, + method_factory_.NewRunnableMethod( + &SyncScheduler::ScheduleNudgeImpl, delay, + GetUpdatesFromNudgeSource(source), types_with_payloads, false, + nudge_location)); } -void SyncerThread::ScheduleNudgeWithPayloads(const TimeDelta& delay, +void SyncScheduler::ScheduleNudgeWithPayloads(const TimeDelta& delay, NudgeSource source, const ModelTypePayloadMap& types_with_payloads, const tracked_objects::Location& nudge_location) { - if (!thread_.IsRunning()) { - NOTREACHED(); - return; - } - + DCHECK_EQ(MessageLoop::current(), sync_loop_); SVLOG(2) << "Nudge scheduled with payloads (source=" << source << ")"; - thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod( - this, &SyncerThread::ScheduleNudgeImpl, delay, - GetUpdatesFromNudgeSource(source), types_with_payloads, false, - nudge_location)); + sync_loop_->PostTask( + FROM_HERE, + method_factory_.NewRunnableMethod( + &SyncScheduler::ScheduleNudgeImpl, delay, + GetUpdatesFromNudgeSource(source), types_with_payloads, false, + nudge_location)); } -void SyncerThread::ScheduleClearUserDataImpl() { - DCHECK_EQ(MessageLoop::current(), thread_.message_loop()); +void SyncScheduler::ScheduleClearUserDataImpl() { + DCHECK_EQ(MessageLoop::current(), sync_loop_); SyncSession* session = new SyncSession(session_context_.get(), this, SyncSourceInfo(), ModelSafeRoutingInfo(), std::vector<ModelSafeWorker*>()); @@ -368,11 +379,11 @@ void SyncerThread::ScheduleClearUserDataImpl() { SyncSessionJob::CLEAR_USER_DATA, session, FROM_HERE); } -void SyncerThread::ScheduleNudgeImpl(const TimeDelta& delay, +void SyncScheduler::ScheduleNudgeImpl(const TimeDelta& delay, GetUpdatesCallerInfo::GetUpdatesSource source, const ModelTypePayloadMap& types_with_payloads, bool is_canary_job, const tracked_objects::Location& nudge_location) { - DCHECK_EQ(MessageLoop::current(), thread_.message_loop()); + DCHECK_EQ(MessageLoop::current(), sync_loop_); SVLOG(2) << "Running Schedule nudge impl (source=" << source << ")"; // Note we currently nudge for all types regardless of the ones incurring @@ -461,29 +472,27 @@ void GetModelSafeParamsForTypes(const ModelTypeBitSet& types, } } -void SyncerThread::ScheduleConfig(const ModelTypeBitSet& types, - sync_api::ConfigureReason reason) { - if (!thread_.IsRunning()) { - LOG(INFO) << "ScheduleConfig failed because thread is not running."; - NOTREACHED(); - return; - } - +void SyncScheduler::ScheduleConfig(const ModelTypeBitSet& types, + sync_api::ConfigureReason reason) { + DCHECK_EQ(MessageLoop::current(), sync_loop_); SVLOG(2) << "Scheduling a config"; ModelSafeRoutingInfo routes; std::vector<ModelSafeWorker*> workers; GetModelSafeParamsForTypes(types, session_context_->registrar(), &routes, &workers); - thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod( - this, &SyncerThread::ScheduleConfigImpl, routes, workers, - GetSourceFromReason(reason))); + sync_loop_->PostTask( + FROM_HERE, + method_factory_.NewRunnableMethod( + &SyncScheduler::ScheduleConfigImpl, routes, workers, + GetSourceFromReason(reason))); } -void SyncerThread::ScheduleConfigImpl(const ModelSafeRoutingInfo& routing_info, +void SyncScheduler::ScheduleConfigImpl( + const ModelSafeRoutingInfo& routing_info, const std::vector<ModelSafeWorker*>& workers, const sync_pb::GetUpdatesCallerInfo::GetUpdatesSource source) { - DCHECK_EQ(MessageLoop::current(), thread_.message_loop()); + DCHECK_EQ(MessageLoop::current(), sync_loop_); SVLOG(2) << "ScheduleConfigImpl..."; // TODO(tim): config-specific GetUpdatesCallerInfo value? @@ -496,14 +505,14 @@ void SyncerThread::ScheduleConfigImpl(const ModelSafeRoutingInfo& routing_info, SyncSessionJob::CONFIGURATION, session, FROM_HERE); } -void SyncerThread::ScheduleSyncSessionJob(const base::TimeDelta& delay, +void SyncScheduler::ScheduleSyncSessionJob(const base::TimeDelta& delay, SyncSessionJob::SyncSessionJobPurpose purpose, sessions::SyncSession* session, const tracked_objects::Location& nudge_location) { - DCHECK_EQ(MessageLoop::current(), thread_.message_loop()); + DCHECK_EQ(MessageLoop::current(), sync_loop_); SyncSessionJob job(purpose, TimeTicks::Now() + delay, - make_linked_ptr(session), false, nudge_location); + make_linked_ptr(session), false, nudge_location); if (purpose == SyncSessionJob::NUDGE) { SVLOG(2) << "Resetting pending_nudge in ScheduleSyncSessionJob"; DCHECK(!pending_nudge_.get() || pending_nudge_->session.get() == session); @@ -511,14 +520,17 @@ void SyncerThread::ScheduleSyncSessionJob(const base::TimeDelta& delay, } SVLOG(2) << "Posting job to execute in DoSyncSessionJob. Job purpose " << job.purpose; - MessageLoop::current()->PostDelayedTask(FROM_HERE, NewRunnableMethod(this, - &SyncerThread::DoSyncSessionJob, job), + sync_loop_->PostDelayedTask( + FROM_HERE, + method_factory_.NewRunnableMethod( + &SyncScheduler::DoSyncSessionJob, job), delay.InMilliseconds()); } -void SyncerThread::SetSyncerStepsForPurpose( +void SyncScheduler::SetSyncerStepsForPurpose( SyncSessionJob::SyncSessionJobPurpose purpose, SyncerStep* start, SyncerStep* end) { + DCHECK_EQ(MessageLoop::current(), sync_loop_); *end = SYNCER_END; switch (purpose) { case SyncSessionJob::CONFIGURATION: @@ -537,8 +549,8 @@ void SyncerThread::SetSyncerStepsForPurpose( } } -void SyncerThread::DoSyncSessionJob(const SyncSessionJob& job) { - DCHECK_EQ(MessageLoop::current(), thread_.message_loop()); +void SyncScheduler::DoSyncSessionJob(const SyncSessionJob& job) { + DCHECK_EQ(MessageLoop::current(), sync_loop_); if (!ShouldRunJob(job)) { LOG(WARNING) << "Not executing job at DoSyncSessionJob, purpose = " << job.purpose << " source = " @@ -580,7 +592,9 @@ void SyncerThread::DoSyncSessionJob(const SyncSessionJob& job) { FinishSyncSessionJob(job); } -void SyncerThread::UpdateCarryoverSessionState(const SyncSessionJob& old_job) { +void SyncScheduler::UpdateCarryoverSessionState( + const SyncSessionJob& old_job) { + DCHECK_EQ(MessageLoop::current(), sync_loop_); if (old_job.purpose == SyncSessionJob::CONFIGURATION) { // Whatever types were part of a configuration task will have had updates // downloaded. For that reason, we make sure they get recorded in the @@ -599,8 +613,8 @@ void SyncerThread::UpdateCarryoverSessionState(const SyncSessionJob& old_job) { } } -void SyncerThread::FinishSyncSessionJob(const SyncSessionJob& job) { - DCHECK_EQ(MessageLoop::current(), thread_.message_loop()); +void SyncScheduler::FinishSyncSessionJob(const SyncSessionJob& job) { + DCHECK_EQ(MessageLoop::current(), sync_loop_); // Update timing information for how often datatypes are triggering nudges. base::TimeTicks now = TimeTicks::Now(); if (!last_sync_session_end_time_.is_null()) { @@ -624,8 +638,8 @@ void SyncerThread::FinishSyncSessionJob(const SyncSessionJob& job) { ScheduleNextSync(job); } -void SyncerThread::ScheduleNextSync(const SyncSessionJob& old_job) { - DCHECK_EQ(MessageLoop::current(), thread_.message_loop()); +void SyncScheduler::ScheduleNextSync(const SyncSessionJob& old_job) { + DCHECK_EQ(MessageLoop::current(), sync_loop_); DCHECK(!old_job.session->HasMoreToSync()); // Note: |num_server_changes_remaining| > 0 here implies that we received a // broken response while trying to download all updates, because the Syncer @@ -667,7 +681,8 @@ void SyncerThread::ScheduleNextSync(const SyncSessionJob& old_job) { DCHECK_EQ(SyncSessionJob::NUDGE, old_job.purpose); DCHECK(!wait_interval_->had_nudge); wait_interval_->had_nudge = true; - wait_interval_->timer.Reset(); + // Resume waiting. + RestartWaiting(); } else { SVLOG(2) << "Failed. Schedule a job with continuation as source"; // We weren't continuing and we aren't in backoff. Schedule a normal @@ -685,9 +700,8 @@ void SyncerThread::ScheduleNextSync(const SyncSessionJob& old_job) { } } -void SyncerThread::AdjustPolling(const SyncSessionJob* old_job) { - DCHECK(thread_.IsRunning()); - DCHECK_EQ(MessageLoop::current(), thread_.message_loop()); +void SyncScheduler::AdjustPolling(const SyncSessionJob* old_job) { + DCHECK_EQ(MessageLoop::current(), sync_loop_); TimeDelta poll = (!session_context_->notifications_enabled()) ? syncer_short_poll_interval_seconds_ : @@ -703,15 +717,23 @@ void SyncerThread::AdjustPolling(const SyncSessionJob* old_job) { // Adjust poll rate. poll_timer_.Stop(); - poll_timer_.Start(poll, this, &SyncerThread::PollTimerCallback); + poll_timer_.Start(poll, this, &SyncScheduler::PollTimerCallback); +} + +void SyncScheduler::RestartWaiting() { + CHECK(wait_interval_.get()); + wait_interval_->timer.Stop(); + wait_interval_->timer.Start(wait_interval_->length, + this, &SyncScheduler::DoCanaryJob); } -void SyncerThread::HandleConsecutiveContinuationError( +void SyncScheduler::HandleConsecutiveContinuationError( const SyncSessionJob& old_job) { - DCHECK_EQ(MessageLoop::current(), thread_.message_loop()); - // This if conditions should be compiled out in retail builds. - if (IsBackingOff()) { - DCHECK(wait_interval_->timer.IsRunning() || old_job.is_canary_job); + DCHECK_EQ(MessageLoop::current(), sync_loop_); + if (DCHECK_IS_ON()) { + if (IsBackingOff()) { + DCHECK(wait_interval_->timer.IsRunning() || old_job.is_canary_job); + } } TimeDelta length = delay_provider_->GetDelay( @@ -739,11 +761,11 @@ void SyncerThread::HandleConsecutiveContinuationError( // TODO(lipalani) - handle clear user data. InitOrCoalescePendingJob(old_job); } - wait_interval_->timer.Start(length, this, &SyncerThread::DoCanaryJob); + RestartWaiting(); } // static -TimeDelta SyncerThread::GetRecommendedDelay(const TimeDelta& last_delay) { +TimeDelta SyncScheduler::GetRecommendedDelay(const TimeDelta& last_delay) { if (last_delay.InSeconds() >= kMaxBackoffSeconds) return TimeDelta::FromSeconds(kMaxBackoffSeconds); @@ -766,19 +788,30 @@ TimeDelta SyncerThread::GetRecommendedDelay(const TimeDelta& last_delay) { return TimeDelta::FromSeconds(backoff_s); } -void SyncerThread::Stop() { - SVLOG(2) << "stop called"; +void SyncScheduler::RequestEarlyExit() { syncer_->RequestEarlyExit(); // Safe to call from any thread. - session_context_->connection_manager()->RemoveListener(this); - thread_.Stop(); } -void SyncerThread::DoCanaryJob() { +void SyncScheduler::Stop() { + DCHECK_EQ(MessageLoop::current(), sync_loop_); + SVLOG(2) << "Stop called"; + method_factory_.RevokeAll(); + wait_interval_.reset(); + poll_timer_.Stop(); + if (started_) { + session_context_->connection_manager()->RemoveListener(this); + started_ = false; + } +} + +void SyncScheduler::DoCanaryJob() { + DCHECK_EQ(MessageLoop::current(), sync_loop_); SVLOG(2) << "Do canary job"; DoPendingJobIfPossible(true); } -void SyncerThread::DoPendingJobIfPossible(bool is_canary_job) { +void SyncScheduler::DoPendingJobIfPossible(bool is_canary_job) { + DCHECK_EQ(MessageLoop::current(), sync_loop_); SyncSessionJob* job_to_execute = NULL; if (mode_ == CONFIGURATION_MODE && wait_interval_.get() && wait_interval_->pending_configure_job.get()) { @@ -809,7 +842,8 @@ void SyncerThread::DoPendingJobIfPossible(bool is_canary_job) { } } -SyncSession* SyncerThread::CreateSyncSession(const SyncSourceInfo& source) { +SyncSession* SyncScheduler::CreateSyncSession(const SyncSourceInfo& source) { + DCHECK_EQ(MessageLoop::current(), sync_loop_); ModelSafeRoutingInfo routes; std::vector<ModelSafeWorker*> workers; session_context_->registrar()->GetModelSafeRoutingInfo(&routes); @@ -822,8 +856,8 @@ SyncSession* SyncerThread::CreateSyncSession(const SyncSourceInfo& source) { return session; } -void SyncerThread::PollTimerCallback() { - DCHECK_EQ(MessageLoop::current(), thread_.message_loop()); +void SyncScheduler::PollTimerCallback() { + DCHECK_EQ(MessageLoop::current(), sync_loop_); ModelSafeRoutingInfo r; ModelTypePayloadMap types_with_payloads = syncable::ModelTypePayloadMapFromRoutingInfo(r, std::string()); @@ -833,61 +867,70 @@ void SyncerThread::PollTimerCallback() { FROM_HERE); } -void SyncerThread::Unthrottle() { +void SyncScheduler::Unthrottle() { + DCHECK_EQ(MessageLoop::current(), sync_loop_); DCHECK_EQ(WaitInterval::THROTTLED, wait_interval_->mode); SVLOG(2) << "Unthrottled."; DoCanaryJob(); wait_interval_.reset(); } -void SyncerThread::Notify(SyncEngineEvent::EventCause cause) { - DCHECK_EQ(MessageLoop::current(), thread_.message_loop()); +void SyncScheduler::Notify(SyncEngineEvent::EventCause cause) { + DCHECK_EQ(MessageLoop::current(), sync_loop_); session_context_->NotifyListeners(SyncEngineEvent(cause)); } -bool SyncerThread::IsBackingOff() const { +bool SyncScheduler::IsBackingOff() const { + DCHECK_EQ(MessageLoop::current(), sync_loop_); return wait_interval_.get() && wait_interval_->mode == WaitInterval::EXPONENTIAL_BACKOFF; } -void SyncerThread::OnSilencedUntil(const base::TimeTicks& silenced_until) { +void SyncScheduler::OnSilencedUntil(const base::TimeTicks& silenced_until) { + DCHECK_EQ(MessageLoop::current(), sync_loop_); wait_interval_.reset(new WaitInterval(WaitInterval::THROTTLED, silenced_until - TimeTicks::Now())); wait_interval_->timer.Start(wait_interval_->length, this, - &SyncerThread::Unthrottle); + &SyncScheduler::Unthrottle); } -bool SyncerThread::IsSyncingCurrentlySilenced() { +bool SyncScheduler::IsSyncingCurrentlySilenced() { + DCHECK_EQ(MessageLoop::current(), sync_loop_); return wait_interval_.get() && wait_interval_->mode == WaitInterval::THROTTLED; } -void SyncerThread::OnReceivedShortPollIntervalUpdate( +void SyncScheduler::OnReceivedShortPollIntervalUpdate( const base::TimeDelta& new_interval) { - DCHECK_EQ(MessageLoop::current(), thread_.message_loop()); + DCHECK_EQ(MessageLoop::current(), sync_loop_); syncer_short_poll_interval_seconds_ = new_interval; } -void SyncerThread::OnReceivedLongPollIntervalUpdate( +void SyncScheduler::OnReceivedLongPollIntervalUpdate( const base::TimeDelta& new_interval) { - DCHECK_EQ(MessageLoop::current(), thread_.message_loop()); + DCHECK_EQ(MessageLoop::current(), sync_loop_); syncer_long_poll_interval_seconds_ = new_interval; } -void SyncerThread::OnShouldStopSyncingPermanently() { +void SyncScheduler::OnShouldStopSyncingPermanently() { + DCHECK_EQ(MessageLoop::current(), sync_loop_); SVLOG(2) << "OnShouldStopSyncingPermanently"; syncer_->RequestEarlyExit(); // Thread-safe. Notify(SyncEngineEvent::STOP_SYNCING_PERMANENTLY); } -void SyncerThread::OnServerConnectionEvent( +void SyncScheduler::OnServerConnectionEvent( const ServerConnectionEvent& event) { - thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this, - &SyncerThread::CheckServerConnectionManagerStatus, - event.connection_code)); + DCHECK_EQ(MessageLoop::current(), sync_loop_); + sync_loop_->PostTask( + FROM_HERE, + method_factory_.NewRunnableMethod( + &SyncScheduler::CheckServerConnectionManagerStatus, + event.connection_code)); } -void SyncerThread::set_notifications_enabled(bool notifications_enabled) { +void SyncScheduler::set_notifications_enabled(bool notifications_enabled) { + DCHECK_EQ(MessageLoop::current(), sync_loop_); session_context_->set_notifications_enabled(notifications_enabled); } diff --git a/chrome/browser/sync/engine/syncer_thread.h b/chrome/browser/sync/engine/sync_scheduler.h index be7d028..df0521f 100644 --- a/chrome/browser/sync/engine/syncer_thread.h +++ b/chrome/browser/sync/engine/sync_scheduler.h @@ -2,19 +2,19 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // -// A class to run the syncer on a thread. -#ifndef CHROME_BROWSER_SYNC_ENGINE_SYNCER_THREAD_H_ -#define CHROME_BROWSER_SYNC_ENGINE_SYNCER_THREAD_H_ +// A class to schedule syncer tasks intelligently. +#ifndef CHROME_BROWSER_SYNC_ENGINE_SYNC_SCHEDULER_H_ +#define CHROME_BROWSER_SYNC_ENGINE_SYNC_SCHEDULER_H_ #pragma once #include <string> #include "base/callback.h" +#include "base/gtest_prod_util.h" #include "base/memory/linked_ptr.h" #include "base/memory/scoped_ptr.h" #include "base/observer_list.h" #include "base/task.h" -#include "base/threading/thread.h" #include "base/time.h" #include "base/timer.h" #include "chrome/browser/sync/engine/configure_reason.h" @@ -27,12 +27,14 @@ #include "chrome/browser/sync/sessions/sync_session.h" #include "chrome/browser/sync/sessions/sync_session_context.h" +class MessageLoop; + namespace browser_sync { struct ServerConnectionEvent; -class SyncerThread : public sessions::SyncSession::Delegate, - public ServerConnectionEventListener { +class SyncScheduler : public sessions::SyncSession::Delegate, + public ServerConnectionEventListener { public: enum Mode { // In this mode, the thread only performs configuration tasks. This is @@ -45,29 +47,35 @@ class SyncerThread : public sessions::SyncSession::Delegate, NORMAL_MODE, }; + // All methods of SyncScheduler must be called on the same thread + // (except for RequestEarlyExit()). + // |name| is a display string to identify the syncer thread. Takes // |ownership of both |context| and |syncer|. - SyncerThread(const std::string& name, - sessions::SyncSessionContext* context, Syncer* syncer); - virtual ~SyncerThread(); + SyncScheduler(const std::string& name, + sessions::SyncSessionContext* context, Syncer* syncer); + + // Calls Stop(). + virtual ~SyncScheduler(); typedef Callback0::Type ModeChangeCallback; - // Change the mode of operation. - // We don't use a lock when changing modes, so we won't cause currently - // scheduled jobs to adhere to the new mode. We could protect it, but it - // doesn't buy very much as a) a session could already be in progress and it - // will continue no matter what, b) the scheduled sessions already contain - // all their required state and won't be affected by potential change at - // higher levels (i.e. the registrar), and c) we service tasks FIFO, so once - // the mode changes all future jobs will be run against the updated mode. - // If supplied, |callback| will be invoked when the mode has been - // changed to |mode| *from the SyncerThread*, and not from the caller - // thread. + // Start the scheduler with the given mode. If the scheduler is + // already started, switch to the given mode, although some + // scheduled tasks from the old mode may still run. If non-NULL, + // |callback| will be invoked when the mode has been changed to + // |mode|. Takes ownership of |callback|. void Start(Mode mode, ModeChangeCallback* callback); - // Joins on the thread as soon as possible (currently running session - // completes). + // Request that any running syncer task stop as soon as possible. + // This function can be called from any thread. Stop must still be + // called to stop future schedule tasks. + // + // TODO(akalin): This function is awkward. Find a better way to let + // the UI thread stop the syncer thread. + void RequestEarlyExit(); + + // Cancel all scheduled tasks. Can be called even if already stopped. void Stop(); // The meat and potatoes. @@ -145,25 +153,25 @@ class SyncerThread : public sessions::SyncSession::Delegate, // that came in. tracked_objects::Location nudge_location; }; - friend class SyncerThreadTest; - friend class SyncerThreadWhiteboxTest; + friend class SyncSchedulerTest; + friend class SyncSchedulerWhiteboxTest; - FRIEND_TEST_ALL_PREFIXES(SyncerThreadWhiteboxTest, + FRIEND_TEST_ALL_PREFIXES(SyncSchedulerWhiteboxTest, DropNudgeWhileExponentialBackOff); - FRIEND_TEST_ALL_PREFIXES(SyncerThreadWhiteboxTest, SaveNudge); - FRIEND_TEST_ALL_PREFIXES(SyncerThreadWhiteboxTest, ContinueNudge); - FRIEND_TEST_ALL_PREFIXES(SyncerThreadWhiteboxTest, DropPoll); - FRIEND_TEST_ALL_PREFIXES(SyncerThreadWhiteboxTest, ContinuePoll); - FRIEND_TEST_ALL_PREFIXES(SyncerThreadWhiteboxTest, ContinueConfiguration); - FRIEND_TEST_ALL_PREFIXES(SyncerThreadWhiteboxTest, + FRIEND_TEST_ALL_PREFIXES(SyncSchedulerWhiteboxTest, SaveNudge); + FRIEND_TEST_ALL_PREFIXES(SyncSchedulerWhiteboxTest, ContinueNudge); + FRIEND_TEST_ALL_PREFIXES(SyncSchedulerWhiteboxTest, DropPoll); + FRIEND_TEST_ALL_PREFIXES(SyncSchedulerWhiteboxTest, ContinuePoll); + FRIEND_TEST_ALL_PREFIXES(SyncSchedulerWhiteboxTest, ContinueConfiguration); + FRIEND_TEST_ALL_PREFIXES(SyncSchedulerWhiteboxTest, SaveConfigurationWhileThrottled); - FRIEND_TEST_ALL_PREFIXES(SyncerThreadWhiteboxTest, + FRIEND_TEST_ALL_PREFIXES(SyncSchedulerWhiteboxTest, SaveNudgeWhileThrottled); - FRIEND_TEST_ALL_PREFIXES(SyncerThreadWhiteboxTest, + FRIEND_TEST_ALL_PREFIXES(SyncSchedulerWhiteboxTest, ContinueClearUserDataUnderAllCircumstances); - FRIEND_TEST_ALL_PREFIXES(SyncerThreadWhiteboxTest, + FRIEND_TEST_ALL_PREFIXES(SyncSchedulerWhiteboxTest, ContinueCanaryJobConfig); - FRIEND_TEST_ALL_PREFIXES(SyncerThreadWhiteboxTest, + FRIEND_TEST_ALL_PREFIXES(SyncSchedulerWhiteboxTest, ContinueNudgeWhileExponentialBackOff); // A component used to get time delays associated with exponential backoff. @@ -196,7 +204,7 @@ class SyncerThread : public sessions::SyncSession::Delegate, // interval and mode == EXPONENTIAL_BACKOFF. bool had_nudge; base::TimeDelta length; - base::OneShotTimer<SyncerThread> timer; + base::OneShotTimer<SyncScheduler> timer; // Configure jobs are saved only when backing off or throttling. So we // expose the pointer here. @@ -228,6 +236,9 @@ class SyncerThread : public sessions::SyncSession::Delegate, // Helper to configure polling intervals. Used by Start and ScheduleNextSync. void AdjustPolling(const SyncSessionJob* old_job); + // Helper to restart waiting with |wait_interval_|'s timer. + void RestartWaiting(); + // Helper to ScheduleNextSync in case of consecutive sync errors. void HandleConsecutiveContinuationError(const SyncSessionJob& old_job); @@ -302,9 +313,17 @@ class SyncerThread : public sessions::SyncSession::Delegate, // the client starts up and does not need to perform an initial sync. void SendInitialSnapshot(); + ScopedRunnableMethodFactory<SyncScheduler> method_factory_; + + // Used for logging. const std::string name_; - base::Thread thread_; + // The message loop this object is on. Almost all methods have to + // be called on this thread. + MessageLoop* const sync_loop_; + + // Set in Start(), unset in Stop(). + bool started_; // Modifiable versions of kDefaultLongPollIntervalSeconds which can be // updated by the server. @@ -312,9 +331,9 @@ class SyncerThread : public sessions::SyncSession::Delegate, base::TimeDelta syncer_long_poll_interval_seconds_; // Periodic timer for polling. See AdjustPolling. - base::RepeatingTimer<SyncerThread> poll_timer_; + base::RepeatingTimer<SyncScheduler> poll_timer_; - // The mode of operation. We don't use a lock, see Start(...) comment. + // The mode of operation. Mode mode_; // TODO(tim): Bug 26339. This needs to track more than just time I think, @@ -337,13 +356,9 @@ class SyncerThread : public sessions::SyncSession::Delegate, scoped_ptr<sessions::SyncSessionContext> session_context_; - DISALLOW_COPY_AND_ASSIGN(SyncerThread); + DISALLOW_COPY_AND_ASSIGN(SyncScheduler); }; } // namespace browser_sync -// The SyncerThread manages its own internal thread and thus outlives it. We -// don't need refcounting for posting tasks to this internal thread. -DISABLE_RUNNABLE_METHOD_REFCOUNT(browser_sync::SyncerThread); - -#endif // CHROME_BROWSER_SYNC_ENGINE_SYNCER_THREAD_H_ +#endif // CHROME_BROWSER_SYNC_ENGINE_SYNC_SCHEDULER_H_ diff --git a/chrome/browser/sync/engine/syncer_thread_unittest.cc b/chrome/browser/sync/engine/sync_scheduler_unittest.cc index a2ce04e..d7310d5 100644 --- a/chrome/browser/sync/engine/syncer_thread_unittest.cc +++ b/chrome/browser/sync/engine/sync_scheduler_unittest.cc @@ -2,12 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/synchronization/waitable_event.h" +#include "base/bind.h" +#include "base/callback.h" +#include "base/compiler_specific.h" +#include "base/memory/scoped_callback_factory.h" +#include "base/message_loop.h" +#include "base/task.h" #include "base/test/test_timeouts.h" #include "chrome/browser/sync/engine/mock_model_safe_workers.h" #include "chrome/browser/sync/engine/configure_reason.h" +#include "chrome/browser/sync/engine/sync_scheduler.h" #include "chrome/browser/sync/engine/syncer.h" -#include "chrome/browser/sync/engine/syncer_thread.h" #include "chrome/browser/sync/sessions/test_util.h" #include "chrome/test/sync/engine/mock_connection_manager.h" #include "chrome/test/sync/engine/test_directory_setter_upper.h" @@ -44,11 +49,37 @@ struct SyncShareRecords { std::vector<linked_ptr<SyncSessionSnapshot> > snapshots; }; +void QuitLoopNow() { + // We use QuitNow() instead of Quit() as the latter may get stalled + // indefinitely in the presence of repeated timers with low delays + // and a slow test (e.g., ThrottlingDoesThrottle [which has a poll + // delay of 5ms] run under TSAN on the trybots). + MessageLoop::current()->QuitNow(); +} + +void RunLoop() { + MessageLoop::current()->Run(); +} + +void PumpLoop() { + // Do it this way instead of RunAllPending to pump loop exactly once + // (necessary in the presence of timers; see comment in + // QuitLoopNow). + MessageLoop::current()->PostTask(FROM_HERE, base::Bind(&QuitLoopNow)); + RunLoop(); +} + // Convenient to use in tests wishing to analyze SyncShare calls over time. static const size_t kMinNumSamples = 5; -class SyncerThreadTest : public testing::Test { +class SyncSchedulerTest : public testing::Test { public: - class MockDelayProvider : public SyncerThread::DelayProvider { + SyncSchedulerTest() + : callback_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), + context_(NULL), + syncer_(NULL), + delay_(NULL) {} + + class MockDelayProvider : public SyncScheduler::DelayProvider { public: MOCK_METHOD1(GetDelay, TimeDelta(const TimeDelta&)); }; @@ -70,11 +101,11 @@ class SyncerThreadTest : public testing::Test { registrar_.get(), std::vector<SyncEngineEventListener*>()); context_->set_notifications_enabled(true); context_->set_account_name("Test"); - syncer_thread_.reset( - new SyncerThread("TestSyncerThread", context_, syncer_)); + scheduler_.reset( + new SyncScheduler("TestSyncScheduler", context_, syncer_)); } - SyncerThread* syncer_thread() { return syncer_thread_.get(); } + SyncScheduler* scheduler() { return scheduler_.get(); } MockSyncer* syncer() { return syncer_; } MockDelayProvider* delay() { return delay_; } MockConnectionManager* connection() { return connection_.get(); } @@ -84,7 +115,9 @@ class SyncerThreadTest : public testing::Test { } virtual void TearDown() { - syncer_thread()->Stop(); + PumpLoop(); + scheduler_.reset(); + PumpLoop(); syncdb_.TearDown(); } @@ -101,16 +134,32 @@ class SyncerThreadTest : public testing::Test { } } - bool GetBackoffAndResetTest(base::WaitableEvent* done) { + void DoQuitLoopNow() { + QuitLoopNow(); + } + + void StartSyncScheduler(SyncScheduler::Mode mode) { + scheduler()->Start( + mode, + callback_factory_.NewCallback(&SyncSchedulerTest::DoQuitLoopNow)); + } + + bool GetBackoffAndResetTest() { syncable::ModelTypeBitSet nudge_types; - syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL); - syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, nudge_types, - FROM_HERE); - done->TimedWait(timeout()); - TearDown(); - done->Reset(); + StartSyncScheduler(SyncScheduler::NORMAL_MODE); + RunLoop(); + + scheduler()->ScheduleNudge( + zero(), NUDGE_SOURCE_LOCAL, nudge_types, FROM_HERE); + RunLoop(); + + bool backing_off = scheduler()->IsBackingOff(); + scheduler()->Stop(); + syncdb_.TearDown(); + Mock::VerifyAndClearExpectations(syncer()); - bool backing_off = syncer_thread()->IsBackingOff(); + + TearDown(); SetUp(); UseMockDelayProvider(); EXPECT_CALL(*delay(), GetDelay(_)) @@ -120,26 +169,7 @@ class SyncerThreadTest : public testing::Test { void UseMockDelayProvider() { delay_ = new MockDelayProvider(); - syncer_thread_->delay_provider_.reset(delay_); - } - - void PostSignalTask(base::WaitableEvent* done) { - syncer_thread_->thread_.message_loop()->PostTask(FROM_HERE, - NewRunnableFunction(&SyncerThreadTest::SignalWaitableEvent, done)); - } - - void FlushLastTask(base::WaitableEvent* done) { - PostSignalTask(done); - done->TimedWait(timeout()); - done->Reset(); - } - - static void SignalWaitableEvent(base::WaitableEvent* event) { - event->Signal(); - } - - static void QuitMessageLoop() { - MessageLoop::current()->Quit(); + scheduler_->delay_provider_.reset(delay_); } // Compare a ModelTypeBitSet to a ModelTypePayloadMap, ignoring @@ -161,7 +191,9 @@ class SyncerThreadTest : public testing::Test { SyncSessionContext* context() { return context_; } private: - scoped_ptr<SyncerThread> syncer_thread_; + base::ScopedCallbackFactory<SyncSchedulerTest> callback_factory_; + MessageLoop message_loop_; + scoped_ptr<SyncScheduler> scheduler_; scoped_ptr<MockConnectionManager> connection_; SyncSessionContext* context_; MockSyncer* syncer_; @@ -170,62 +202,72 @@ class SyncerThreadTest : public testing::Test { MockDirectorySetterUpper syncdb_; }; -bool RecordSyncShareImpl(SyncSession* s, SyncShareRecords* record, - size_t signal_after) { +void RecordSyncShareImpl(SyncSession* s, SyncShareRecords* record) { record->times.push_back(TimeTicks::Now()); record->snapshots.push_back(make_linked_ptr(new SyncSessionSnapshot( s->TakeSnapshot()))); - return record->times.size() >= signal_after; } -ACTION_P4(RecordSyncShareAndPostSignal, record, signal_after, test, event) { - if (RecordSyncShareImpl(arg0, record, signal_after) && event) - test->PostSignalTask(event); +ACTION_P(RecordSyncShare, record) { + RecordSyncShareImpl(arg0, record); + QuitLoopNow(); } -ACTION_P3(RecordSyncShare, record, signal_after, event) { - if (RecordSyncShareImpl(arg0, record, signal_after) && event) - event->Signal(); +ACTION_P2(RecordSyncShareMultiple, record, quit_after) { + RecordSyncShareImpl(arg0, record); + EXPECT_LE(record->times.size(), quit_after); + if (record->times.size() >= quit_after) { + QuitLoopNow(); + } } -ACTION_P(SignalEvent, event) { - SyncerThreadTest::SignalWaitableEvent(event); +ACTION(AddFailureAndQuitLoopNow) { + ADD_FAILURE(); + QuitLoopNow(); +} + +ACTION(QuitLoopNowAction) { + QuitLoopNow(); } // Test nudge scheduling. -TEST_F(SyncerThreadTest, Nudge) { - syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL); - base::WaitableEvent done(false, false); +TEST_F(SyncSchedulerTest, Nudge) { SyncShareRecords records; syncable::ModelTypeBitSet model_types; model_types[syncable::BOOKMARKS] = true; EXPECT_CALL(*syncer(), SyncShare(_,_,_)) .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), - WithArg<0>(RecordSyncShare(&records, 1U, &done)))) + WithArg<0>(RecordSyncShare(&records)))) .RetiresOnSaturation(); - syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, model_types, - FROM_HERE); - done.TimedWait(timeout()); - EXPECT_EQ(1U, records.snapshots.size()); + StartSyncScheduler(SyncScheduler::NORMAL_MODE); + RunLoop(); + + scheduler()->ScheduleNudge( + zero(), NUDGE_SOURCE_LOCAL, model_types, FROM_HERE); + RunLoop(); + + ASSERT_EQ(1U, records.snapshots.size()); EXPECT_TRUE(CompareModelTypeBitSetToModelTypePayloadMap(model_types, records.snapshots[0]->source.types)); EXPECT_EQ(GetUpdatesCallerInfo::LOCAL, records.snapshots[0]->source.updates_source); + Mock::VerifyAndClearExpectations(syncer()); + // Make sure a second, later, nudge is unaffected by first (no coalescing). SyncShareRecords records2; model_types[syncable::BOOKMARKS] = false; model_types[syncable::AUTOFILL] = true; EXPECT_CALL(*syncer(), SyncShare(_,_,_)) .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), - WithArg<0>(RecordSyncShare(&records2, 1U, &done)))); - syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, model_types, - FROM_HERE); - done.TimedWait(timeout()); + WithArg<0>(RecordSyncShare(&records2)))); + scheduler()->ScheduleNudge( + zero(), NUDGE_SOURCE_LOCAL, model_types, FROM_HERE); + RunLoop(); - EXPECT_EQ(1U, records2.snapshots.size()); + ASSERT_EQ(1U, records2.snapshots.size()); EXPECT_TRUE(CompareModelTypeBitSetToModelTypePayloadMap(model_types, records2.snapshots[0]->source.types)); EXPECT_EQ(GetUpdatesCallerInfo::LOCAL, @@ -234,23 +276,23 @@ TEST_F(SyncerThreadTest, Nudge) { // Make sure a regular config command is scheduled fine in the absence of any // errors. -TEST_F(SyncerThreadTest, Config) { - base::WaitableEvent done(false, false); +TEST_F(SyncSchedulerTest, Config) { SyncShareRecords records; syncable::ModelTypeBitSet model_types; model_types[syncable::BOOKMARKS] = true; EXPECT_CALL(*syncer(), SyncShare(_,_,_)) .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), - WithArg<0>(RecordSyncShare(&records, 1U, &done)))); + WithArg<0>(RecordSyncShare(&records)))); - syncer_thread()->Start(SyncerThread::CONFIGURATION_MODE, NULL); + StartSyncScheduler(SyncScheduler::CONFIGURATION_MODE); + RunLoop(); - syncer_thread()->ScheduleConfig(model_types, - sync_api::CONFIGURE_REASON_RECONFIGURATION); - done.TimedWait(timeout()); + scheduler()->ScheduleConfig( + model_types, sync_api::CONFIGURE_REASON_RECONFIGURATION); + RunLoop(); - EXPECT_EQ(1U, records.snapshots.size()); + ASSERT_EQ(1U, records.snapshots.size()); EXPECT_TRUE(CompareModelTypeBitSetToModelTypePayloadMap(model_types, records.snapshots[0]->source.types)); EXPECT_EQ(GetUpdatesCallerInfo::RECONFIGURATION, @@ -258,9 +300,7 @@ TEST_F(SyncerThreadTest, Config) { } // Simulate a failure and make sure the config request is retried. -TEST_F(SyncerThreadTest, ConfigWithBackingOff) { - base::WaitableEvent done(false, false); - base::WaitableEvent* dummy = NULL; +TEST_F(SyncSchedulerTest, ConfigWithBackingOff) { UseMockDelayProvider(); EXPECT_CALL(*delay(), GetDelay(_)) .WillRepeatedly(Return(TimeDelta::FromMilliseconds(1))); @@ -270,17 +310,22 @@ TEST_F(SyncerThreadTest, ConfigWithBackingOff) { EXPECT_CALL(*syncer(), SyncShare(_,_,_)) .WillOnce(DoAll(Invoke(sessions::test_util::SimulateCommitFailed), - WithArg<0>(RecordSyncShare(&records, 1U, dummy)))) + WithArg<0>(RecordSyncShare(&records)))) .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), - WithArg<0>(RecordSyncShare(&records, 1U, &done)))); + WithArg<0>(RecordSyncShare(&records)))); - syncer_thread()->Start(SyncerThread::CONFIGURATION_MODE, NULL); + StartSyncScheduler(SyncScheduler::CONFIGURATION_MODE); + RunLoop(); - syncer_thread()->ScheduleConfig(model_types, - sync_api::CONFIGURE_REASON_RECONFIGURATION); - done.TimedWait(timeout()); + ASSERT_EQ(0U, records.snapshots.size()); + scheduler()->ScheduleConfig( + model_types, sync_api::CONFIGURE_REASON_RECONFIGURATION); + RunLoop(); - EXPECT_EQ(2U, records.snapshots.size()); + ASSERT_EQ(1U, records.snapshots.size()); + RunLoop(); + + ASSERT_EQ(2U, records.snapshots.size()); EXPECT_TRUE(CompareModelTypeBitSetToModelTypePayloadMap(model_types, records.snapshots[1]->source.types)); EXPECT_EQ(GetUpdatesCallerInfo::SYNC_CYCLE_CONTINUATION, @@ -289,13 +334,10 @@ TEST_F(SyncerThreadTest, ConfigWithBackingOff) { // Issue 2 config commands. Second one right after the first has failed // and make sure LATEST is executed. -TEST_F(SyncerThreadTest, MultipleConfigWithBackingOff) { +TEST_F(SyncSchedulerTest, MultipleConfigWithBackingOff) { syncable::ModelTypeBitSet model_types1, model_types2; model_types1[syncable::BOOKMARKS] = true; model_types2[syncable::AUTOFILL] = true; - base::WaitableEvent done(false, false); - base::WaitableEvent done1(false, false); - base::WaitableEvent* dummy = NULL; UseMockDelayProvider(); EXPECT_CALL(*delay(), GetDelay(_)) .WillRepeatedly(Return(TimeDelta::FromMilliseconds(30))); @@ -303,24 +345,29 @@ TEST_F(SyncerThreadTest, MultipleConfigWithBackingOff) { EXPECT_CALL(*syncer(), SyncShare(_,_,_)) .WillOnce(DoAll(Invoke(sessions::test_util::SimulateCommitFailed), - WithArg<0>(RecordSyncShare(&records, 1U, dummy)))) + WithArg<0>(RecordSyncShare(&records)))) .WillOnce(DoAll(Invoke(sessions::test_util::SimulateCommitFailed), - WithArg<0>(RecordSyncShare(&records, 1U, &done1)))) + WithArg<0>(RecordSyncShare(&records)))) .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), - WithArg<0>(RecordSyncShare(&records, 1U, &done)))); + WithArg<0>(RecordSyncShare(&records)))); + + StartSyncScheduler(SyncScheduler::CONFIGURATION_MODE); + RunLoop(); - syncer_thread()->Start(SyncerThread::CONFIGURATION_MODE, NULL); + ASSERT_EQ(0U, records.snapshots.size()); + scheduler()->ScheduleConfig( + model_types1, sync_api::CONFIGURE_REASON_RECONFIGURATION); + RunLoop(); - syncer_thread()->ScheduleConfig(model_types1, - sync_api::CONFIGURE_REASON_RECONFIGURATION); + ASSERT_EQ(1U, records.snapshots.size()); + scheduler()->ScheduleConfig( + model_types2, sync_api::CONFIGURE_REASON_RECONFIGURATION); + RunLoop(); - // done1 indicates the first config failed. - done1.TimedWait(timeout()); - syncer_thread()->ScheduleConfig(model_types2, - sync_api::CONFIGURE_REASON_RECONFIGURATION); - done.TimedWait(timeout()); + ASSERT_EQ(2U, records.snapshots.size()); + RunLoop(); - EXPECT_EQ(3U, records.snapshots.size()); + ASSERT_EQ(3U, records.snapshots.size()); EXPECT_TRUE(CompareModelTypeBitSetToModelTypePayloadMap(model_types2, records.snapshots[2]->source.types)); EXPECT_EQ(GetUpdatesCallerInfo::RECONFIGURATION, @@ -329,13 +376,9 @@ TEST_F(SyncerThreadTest, MultipleConfigWithBackingOff) { // Issue a nudge when the config has failed. Make sure both the config and // nudge are executed. -TEST_F(SyncerThreadTest, NudgeWithConfigWithBackingOff) { +TEST_F(SyncSchedulerTest, NudgeWithConfigWithBackingOff) { syncable::ModelTypeBitSet model_types; model_types[syncable::BOOKMARKS] = true; - base::WaitableEvent done(false, false); - base::WaitableEvent done1(false, false); - base::WaitableEvent done2(false, false); - base::WaitableEvent* dummy = NULL; UseMockDelayProvider(); EXPECT_CALL(*delay(), GetDelay(_)) .WillRepeatedly(Return(TimeDelta::FromMilliseconds(50))); @@ -343,27 +386,36 @@ TEST_F(SyncerThreadTest, NudgeWithConfigWithBackingOff) { EXPECT_CALL(*syncer(), SyncShare(_,_,_)) .WillOnce(DoAll(Invoke(sessions::test_util::SimulateCommitFailed), - WithArg<0>(RecordSyncShare(&records, 1U, dummy)))) + WithArg<0>(RecordSyncShare(&records)))) .WillOnce(DoAll(Invoke(sessions::test_util::SimulateCommitFailed), - WithArg<0>(RecordSyncShare(&records, 1U, &done1)))) + WithArg<0>(RecordSyncShare(&records)))) .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), - WithArg<0>(RecordSyncShare(&records, 1U, &done2)))) + WithArg<0>(RecordSyncShare(&records)))) .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), - WithArg<0>(RecordSyncShare(&records, 1U, &done)))); + WithArg<0>(RecordSyncShare(&records)))); + + StartSyncScheduler(SyncScheduler::CONFIGURATION_MODE); + RunLoop(); + + ASSERT_EQ(0U, records.snapshots.size()); + scheduler()->ScheduleConfig( + model_types, sync_api::CONFIGURE_REASON_RECONFIGURATION); + RunLoop(); + + ASSERT_EQ(1U, records.snapshots.size()); + scheduler()->ScheduleNudge( + zero(), NUDGE_SOURCE_LOCAL, model_types, FROM_HERE); + RunLoop(); - syncer_thread()->Start(SyncerThread::CONFIGURATION_MODE, NULL); + ASSERT_EQ(2U, records.snapshots.size()); + RunLoop(); - syncer_thread()->ScheduleConfig(model_types, - sync_api::CONFIGURE_REASON_RECONFIGURATION); - done1.TimedWait(timeout()); - syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, model_types, - FROM_HERE); + // Now change the mode so nudge can execute. + ASSERT_EQ(3U, records.snapshots.size()); + StartSyncScheduler(SyncScheduler::NORMAL_MODE); + RunLoop(); - // done2 indicates config suceeded. Now change the mode so nudge can execute. - done2.TimedWait(timeout()); - syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL); - done.TimedWait(timeout()); - EXPECT_EQ(4U, records.snapshots.size()); + ASSERT_EQ(4U, records.snapshots.size()); EXPECT_TRUE(CompareModelTypeBitSetToModelTypePayloadMap(model_types, records.snapshots[2]->source.types)); @@ -377,43 +429,45 @@ TEST_F(SyncerThreadTest, NudgeWithConfigWithBackingOff) { } - // Test that nudges are coalesced. -TEST_F(SyncerThreadTest, NudgeCoalescing) { - syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL); - base::WaitableEvent done(false, false); +TEST_F(SyncSchedulerTest, NudgeCoalescing) { + StartSyncScheduler(SyncScheduler::NORMAL_MODE); + RunLoop(); + SyncShareRecords r; EXPECT_CALL(*syncer(), SyncShare(_,_,_)) .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), - WithArg<0>(RecordSyncShare(&r, 1U, &done)))); + WithArg<0>(RecordSyncShare(&r)))); syncable::ModelTypeBitSet types1, types2, types3; types1[syncable::BOOKMARKS] = true; types2[syncable::AUTOFILL] = true; types3[syncable::THEMES] = true; - TimeDelta delay = TimeDelta::FromMilliseconds( - TestTimeouts::tiny_timeout_ms()); + TimeDelta delay = zero(); TimeTicks optimal_time = TimeTicks::Now() + delay; - syncer_thread()->ScheduleNudge(delay, NUDGE_SOURCE_UNKNOWN, types1, - FROM_HERE); - syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, types2, - FROM_HERE); - done.TimedWait(timeout()); + scheduler()->ScheduleNudge( + delay, NUDGE_SOURCE_UNKNOWN, types1, FROM_HERE); + scheduler()->ScheduleNudge( + zero(), NUDGE_SOURCE_LOCAL, types2, FROM_HERE); + RunLoop(); - EXPECT_EQ(1U, r.snapshots.size()); + ASSERT_EQ(1U, r.snapshots.size()); EXPECT_GE(r.times[0], optimal_time); EXPECT_TRUE(CompareModelTypeBitSetToModelTypePayloadMap( types1 | types2, r.snapshots[0]->source.types)); EXPECT_EQ(GetUpdatesCallerInfo::LOCAL, r.snapshots[0]->source.updates_source); + Mock::VerifyAndClearExpectations(syncer()); + SyncShareRecords r2; EXPECT_CALL(*syncer(), SyncShare(_,_,_)) .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), - WithArg<0>(RecordSyncShare(&r2, 1U, &done)))); - syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_NOTIFICATION, types3, - FROM_HERE); - done.TimedWait(timeout()); - EXPECT_EQ(1U, r2.snapshots.size()); + WithArg<0>(RecordSyncShare(&r2)))); + scheduler()->ScheduleNudge( + zero(), NUDGE_SOURCE_NOTIFICATION, types3, FROM_HERE); + RunLoop(); + + ASSERT_EQ(1U, r2.snapshots.size()); EXPECT_TRUE(CompareModelTypeBitSetToModelTypePayloadMap(types3, r2.snapshots[0]->source.types)); EXPECT_EQ(GetUpdatesCallerInfo::NOTIFICATION, @@ -421,65 +475,68 @@ TEST_F(SyncerThreadTest, NudgeCoalescing) { } // Test nudge scheduling. -TEST_F(SyncerThreadTest, NudgeWithPayloads) { - syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL); - base::WaitableEvent done(false, false); +TEST_F(SyncSchedulerTest, NudgeWithPayloads) { + StartSyncScheduler(SyncScheduler::NORMAL_MODE); + RunLoop(); + SyncShareRecords records; syncable::ModelTypePayloadMap model_types_with_payloads; model_types_with_payloads[syncable::BOOKMARKS] = "test"; EXPECT_CALL(*syncer(), SyncShare(_,_,_)) .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), - WithArg<0>(RecordSyncShare(&records, 1U, &done)))) + WithArg<0>(RecordSyncShare(&records)))) .RetiresOnSaturation(); - syncer_thread()->ScheduleNudgeWithPayloads(zero(), NUDGE_SOURCE_LOCAL, - model_types_with_payloads, FROM_HERE); - done.TimedWait(timeout()); + scheduler()->ScheduleNudgeWithPayloads( + zero(), NUDGE_SOURCE_LOCAL, model_types_with_payloads, FROM_HERE); + RunLoop(); - EXPECT_EQ(1U, records.snapshots.size()); + ASSERT_EQ(1U, records.snapshots.size()); EXPECT_EQ(model_types_with_payloads, records.snapshots[0]->source.types); EXPECT_EQ(GetUpdatesCallerInfo::LOCAL, records.snapshots[0]->source.updates_source); + Mock::VerifyAndClearExpectations(syncer()); + // Make sure a second, later, nudge is unaffected by first (no coalescing). SyncShareRecords records2; model_types_with_payloads.erase(syncable::BOOKMARKS); model_types_with_payloads[syncable::AUTOFILL] = "test2"; EXPECT_CALL(*syncer(), SyncShare(_,_,_)) .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), - WithArg<0>(RecordSyncShare(&records2, 1U, &done)))); - syncer_thread()->ScheduleNudgeWithPayloads(zero(), NUDGE_SOURCE_LOCAL, - model_types_with_payloads, FROM_HERE); - done.TimedWait(timeout()); + WithArg<0>(RecordSyncShare(&records2)))); + scheduler()->ScheduleNudgeWithPayloads( + zero(), NUDGE_SOURCE_LOCAL, model_types_with_payloads, FROM_HERE); + RunLoop(); - EXPECT_EQ(1U, records2.snapshots.size()); + ASSERT_EQ(1U, records2.snapshots.size()); EXPECT_EQ(model_types_with_payloads, records2.snapshots[0]->source.types); EXPECT_EQ(GetUpdatesCallerInfo::LOCAL, records2.snapshots[0]->source.updates_source); } // Test that nudges are coalesced. -TEST_F(SyncerThreadTest, NudgeWithPayloadsCoalescing) { - syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL); - base::WaitableEvent done(false, false); +TEST_F(SyncSchedulerTest, NudgeWithPayloadsCoalescing) { + StartSyncScheduler(SyncScheduler::NORMAL_MODE); + RunLoop(); + SyncShareRecords r; EXPECT_CALL(*syncer(), SyncShare(_,_,_)) .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), - WithArg<0>(RecordSyncShare(&r, 1U, &done)))); + WithArg<0>(RecordSyncShare(&r)))); syncable::ModelTypePayloadMap types1, types2, types3; types1[syncable::BOOKMARKS] = "test1"; types2[syncable::AUTOFILL] = "test2"; types3[syncable::THEMES] = "test3"; - TimeDelta delay = TimeDelta::FromMilliseconds( - TestTimeouts::tiny_timeout_ms()); + TimeDelta delay = zero(); TimeTicks optimal_time = TimeTicks::Now() + delay; - syncer_thread()->ScheduleNudgeWithPayloads(delay, NUDGE_SOURCE_UNKNOWN, - types1, FROM_HERE); - syncer_thread()->ScheduleNudgeWithPayloads(zero(), NUDGE_SOURCE_LOCAL, - types2, FROM_HERE); - done.TimedWait(timeout()); + scheduler()->ScheduleNudgeWithPayloads( + delay, NUDGE_SOURCE_UNKNOWN, types1, FROM_HERE); + scheduler()->ScheduleNudgeWithPayloads( + zero(), NUDGE_SOURCE_LOCAL, types2, FROM_HERE); + RunLoop(); - EXPECT_EQ(1U, r.snapshots.size()); + ASSERT_EQ(1U, r.snapshots.size()); EXPECT_GE(r.times[0], optimal_time); syncable::ModelTypePayloadMap coalesced_types; syncable::CoalescePayloads(&coalesced_types, types1); @@ -488,121 +545,138 @@ TEST_F(SyncerThreadTest, NudgeWithPayloadsCoalescing) { EXPECT_EQ(GetUpdatesCallerInfo::LOCAL, r.snapshots[0]->source.updates_source); + Mock::VerifyAndClearExpectations(syncer()); + SyncShareRecords r2; EXPECT_CALL(*syncer(), SyncShare(_,_,_)) .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), - WithArg<0>(RecordSyncShare(&r2, 1U, &done)))); - syncer_thread()->ScheduleNudgeWithPayloads(zero(), NUDGE_SOURCE_NOTIFICATION, - types3, FROM_HERE); - done.TimedWait(timeout()); - EXPECT_EQ(1U, r2.snapshots.size()); + WithArg<0>(RecordSyncShare(&r2)))); + scheduler()->ScheduleNudgeWithPayloads( + zero(), NUDGE_SOURCE_NOTIFICATION, types3, FROM_HERE); + RunLoop(); + + ASSERT_EQ(1U, r2.snapshots.size()); EXPECT_EQ(types3, r2.snapshots[0]->source.types); EXPECT_EQ(GetUpdatesCallerInfo::NOTIFICATION, r2.snapshots[0]->source.updates_source); } // Test that polling works as expected. -TEST_F(SyncerThreadTest, Polling) { +TEST_F(SyncSchedulerTest, Polling) { SyncShareRecords records; - base::WaitableEvent done(false, false); TimeDelta poll_interval(TimeDelta::FromMilliseconds(30)); - syncer_thread()->OnReceivedLongPollIntervalUpdate(poll_interval); EXPECT_CALL(*syncer(), SyncShare(_,_,_)).Times(AtLeast(kMinNumSamples)) .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulateSuccess), - WithArg<0>(RecordSyncShare(&records, kMinNumSamples, &done)))); + WithArg<0>(RecordSyncShareMultiple(&records, kMinNumSamples)))); + + scheduler()->OnReceivedLongPollIntervalUpdate(poll_interval); TimeTicks optimal_start = TimeTicks::Now() + poll_interval; - syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL); - done.TimedWait(timeout()); - syncer_thread()->Stop(); + StartSyncScheduler(SyncScheduler::NORMAL_MODE); + RunLoop(); + + // Run again to wait for polling. + RunLoop(); + scheduler()->Stop(); AnalyzePollRun(records, kMinNumSamples, optimal_start, poll_interval); } // Test that the short poll interval is used. -TEST_F(SyncerThreadTest, PollNotificationsDisabled) { +TEST_F(SyncSchedulerTest, PollNotificationsDisabled) { SyncShareRecords records; - base::WaitableEvent done(false, false); TimeDelta poll_interval(TimeDelta::FromMilliseconds(30)); - syncer_thread()->OnReceivedShortPollIntervalUpdate(poll_interval); - syncer_thread()->set_notifications_enabled(false); EXPECT_CALL(*syncer(), SyncShare(_,_,_)).Times(AtLeast(kMinNumSamples)) .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulateSuccess), - WithArg<0>(RecordSyncShare(&records, kMinNumSamples, &done)))); + WithArg<0>(RecordSyncShareMultiple(&records, kMinNumSamples)))); + + scheduler()->OnReceivedShortPollIntervalUpdate(poll_interval); + scheduler()->set_notifications_enabled(false); TimeTicks optimal_start = TimeTicks::Now() + poll_interval; - syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL); - done.TimedWait(timeout()); - syncer_thread()->Stop(); + StartSyncScheduler(SyncScheduler::NORMAL_MODE); + RunLoop(); + + // Run again to wait for polling. + RunLoop(); + scheduler()->Stop(); AnalyzePollRun(records, kMinNumSamples, optimal_start, poll_interval); } // Test that polling intervals are updated when needed. -TEST_F(SyncerThreadTest, PollIntervalUpdate) { +TEST_F(SyncSchedulerTest, PollIntervalUpdate) { SyncShareRecords records; - base::WaitableEvent done(false, false); TimeDelta poll1(TimeDelta::FromMilliseconds(120)); TimeDelta poll2(TimeDelta::FromMilliseconds(30)); - syncer_thread()->OnReceivedLongPollIntervalUpdate(poll1); + scheduler()->OnReceivedLongPollIntervalUpdate(poll1); EXPECT_CALL(*syncer(), SyncShare(_,_,_)).Times(AtLeast(kMinNumSamples)) .WillOnce(WithArg<0>( sessions::test_util::SimulatePollIntervalUpdate(poll2))) - .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulateSuccess), - WithArg<0>(RecordSyncShare(&records, kMinNumSamples, &done)))); + .WillRepeatedly( + DoAll(Invoke(sessions::test_util::SimulateSuccess), + WithArg<0>( + RecordSyncShareMultiple(&records, kMinNumSamples)))); TimeTicks optimal_start = TimeTicks::Now() + poll1 + poll2; - syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL); - done.TimedWait(timeout()); - syncer_thread()->Stop(); + StartSyncScheduler(SyncScheduler::NORMAL_MODE); + RunLoop(); + + // Run again to wait for polling. + RunLoop(); + scheduler()->Stop(); AnalyzePollRun(records, kMinNumSamples, optimal_start, poll2); } // Test that a sync session is run through to completion. -TEST_F(SyncerThreadTest, HasMoreToSync) { - syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL); - base::WaitableEvent done(false, false); +TEST_F(SyncSchedulerTest, HasMoreToSync) { EXPECT_CALL(*syncer(), SyncShare(_,_,_)) .WillOnce(Invoke(sessions::test_util::SimulateHasMoreToSync)) .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), - SignalEvent(&done))); - syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, ModelTypeBitSet(), - FROM_HERE); - done.TimedWait(timeout()); + QuitLoopNowAction())); + StartSyncScheduler(SyncScheduler::NORMAL_MODE); + RunLoop(); + + scheduler()->ScheduleNudge( + zero(), NUDGE_SOURCE_LOCAL, ModelTypeBitSet(), FROM_HERE); + RunLoop(); // If more nudges are scheduled, they'll be waited on by TearDown, and would // cause our expectation to break. } // Test that no syncing occurs when throttled. -TEST_F(SyncerThreadTest, ThrottlingDoesThrottle) { +TEST_F(SyncSchedulerTest, ThrottlingDoesThrottle) { syncable::ModelTypeBitSet types; types[syncable::BOOKMARKS] = true; - base::WaitableEvent done(false, false); TimeDelta poll(TimeDelta::FromMilliseconds(5)); TimeDelta throttle(TimeDelta::FromMinutes(10)); - syncer_thread()->OnReceivedLongPollIntervalUpdate(poll); + scheduler()->OnReceivedLongPollIntervalUpdate(poll); EXPECT_CALL(*syncer(), SyncShare(_,_,_)) - .WillOnce(WithArg<0>(sessions::test_util::SimulateThrottled(throttle))); + .WillOnce(WithArg<0>(sessions::test_util::SimulateThrottled(throttle))) + .WillRepeatedly(AddFailureAndQuitLoopNow()); + + StartSyncScheduler(SyncScheduler::NORMAL_MODE); + RunLoop(); + + scheduler()->ScheduleNudge( + zero(), NUDGE_SOURCE_LOCAL, types, FROM_HERE); + PumpLoop(); - syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL); - syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, types, - FROM_HERE); - FlushLastTask(&done); + StartSyncScheduler(SyncScheduler::CONFIGURATION_MODE); + RunLoop(); - syncer_thread()->Start(SyncerThread::CONFIGURATION_MODE, NULL); - syncer_thread()->ScheduleConfig(types, - sync_api::CONFIGURE_REASON_RECONFIGURATION); - FlushLastTask(&done); + scheduler()->ScheduleConfig( + types, sync_api::CONFIGURE_REASON_RECONFIGURATION); + PumpLoop(); } -TEST_F(SyncerThreadTest, ThrottlingExpires) { +TEST_F(SyncSchedulerTest, ThrottlingExpires) { SyncShareRecords records; - base::WaitableEvent done(false, false); TimeDelta poll(TimeDelta::FromMilliseconds(15)); TimeDelta throttle1(TimeDelta::FromMilliseconds(150)); TimeDelta throttle2(TimeDelta::FromMinutes(10)); - syncer_thread()->OnReceivedLongPollIntervalUpdate(poll); + scheduler()->OnReceivedLongPollIntervalUpdate(poll); ::testing::InSequence seq; EXPECT_CALL(*syncer(), SyncShare(_,_,_)) @@ -610,110 +684,116 @@ TEST_F(SyncerThreadTest, ThrottlingExpires) { .RetiresOnSaturation(); EXPECT_CALL(*syncer(), SyncShare(_,_,_)) .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulateSuccess), - WithArg<0>(RecordSyncShare(&records, kMinNumSamples, &done)))); + WithArg<0>(RecordSyncShareMultiple(&records, kMinNumSamples)))); TimeTicks optimal_start = TimeTicks::Now() + poll + throttle1; - syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL); - done.TimedWait(timeout()); - syncer_thread()->Stop(); + StartSyncScheduler(SyncScheduler::NORMAL_MODE); + RunLoop(); + // Run again to wait for polling. + RunLoop(); + + scheduler()->Stop(); AnalyzePollRun(records, kMinNumSamples, optimal_start, poll); } // Test nudges / polls don't run in config mode and config tasks do. -TEST_F(SyncerThreadTest, ConfigurationMode) { +TEST_F(SyncSchedulerTest, ConfigurationMode) { TimeDelta poll(TimeDelta::FromMilliseconds(15)); SyncShareRecords records; - base::WaitableEvent done(false, false); - base::WaitableEvent* dummy = NULL; - syncer_thread()->OnReceivedLongPollIntervalUpdate(poll); + scheduler()->OnReceivedLongPollIntervalUpdate(poll); EXPECT_CALL(*syncer(), SyncShare(_,_,_)) .WillOnce((Invoke(sessions::test_util::SimulateSuccess), - WithArg<0>(RecordSyncShare(&records, 1U, dummy)))); - syncer_thread()->Start(SyncerThread::CONFIGURATION_MODE, NULL); + WithArg<0>(RecordSyncShare(&records)))); + + StartSyncScheduler(SyncScheduler::CONFIGURATION_MODE); + RunLoop(); + syncable::ModelTypeBitSet nudge_types; nudge_types[syncable::AUTOFILL] = true; - syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, nudge_types, - FROM_HERE); - syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, nudge_types, - FROM_HERE); + scheduler()->ScheduleNudge( + zero(), NUDGE_SOURCE_LOCAL, nudge_types, FROM_HERE); + scheduler()->ScheduleNudge( + zero(), NUDGE_SOURCE_LOCAL, nudge_types, FROM_HERE); syncable::ModelTypeBitSet config_types; config_types[syncable::BOOKMARKS] = true; - syncer_thread()->ScheduleConfig(config_types, - sync_api::CONFIGURE_REASON_RECONFIGURATION); - FlushLastTask(&done); - syncer_thread()->Stop(); + scheduler()->ScheduleConfig( + config_types, sync_api::CONFIGURE_REASON_RECONFIGURATION); + RunLoop(); - EXPECT_EQ(1U, records.snapshots.size()); + ASSERT_EQ(1U, records.snapshots.size()); EXPECT_TRUE(CompareModelTypeBitSetToModelTypePayloadMap(config_types, records.snapshots[0]->source.types)); } // Test that exponential backoff is properly triggered. -TEST_F(SyncerThreadTest, BackoffTriggers) { - base::WaitableEvent done(false, false); +TEST_F(SyncSchedulerTest, BackoffTriggers) { UseMockDelayProvider(); EXPECT_CALL(*syncer(), SyncShare(_,_,_)) .WillOnce(Invoke(sessions::test_util::SimulateDownloadUpdatesFailed)) .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), - SignalEvent(&done))); - EXPECT_FALSE(GetBackoffAndResetTest(&done)); + QuitLoopNowAction())); + EXPECT_FALSE(GetBackoffAndResetTest()); // Note GetBackoffAndResetTest clears mocks and re-instantiates the syncer. EXPECT_CALL(*syncer(), SyncShare(_,_,_)) .WillOnce(Invoke(sessions::test_util::SimulateCommitFailed)) .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess), - SignalEvent(&done))); - EXPECT_FALSE(GetBackoffAndResetTest(&done)); + QuitLoopNowAction())); + EXPECT_FALSE(GetBackoffAndResetTest()); EXPECT_CALL(*syncer(), SyncShare(_,_,_)) .WillOnce(Invoke(sessions::test_util::SimulateDownloadUpdatesFailed)) .WillRepeatedly(DoAll(Invoke( sessions::test_util::SimulateDownloadUpdatesFailed), - SignalEvent(&done))); - EXPECT_TRUE(GetBackoffAndResetTest(&done)); + QuitLoopNowAction())); + EXPECT_TRUE(GetBackoffAndResetTest()); EXPECT_CALL(*syncer(), SyncShare(_,_,_)) .WillOnce(Invoke(sessions::test_util::SimulateCommitFailed)) .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulateCommitFailed), - SignalEvent(&done))); - EXPECT_TRUE(GetBackoffAndResetTest(&done)); + QuitLoopNowAction())); + EXPECT_TRUE(GetBackoffAndResetTest()); EXPECT_CALL(*syncer(), SyncShare(_,_,_)) .WillOnce(Invoke(sessions::test_util::SimulateDownloadUpdatesFailed)) .WillOnce(Invoke(sessions::test_util::SimulateDownloadUpdatesFailed)) .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulateSuccess), - SignalEvent(&done))); - EXPECT_FALSE(GetBackoffAndResetTest(&done)); + QuitLoopNowAction())); + EXPECT_FALSE(GetBackoffAndResetTest()); EXPECT_CALL(*syncer(), SyncShare(_,_,_)) .WillOnce(Invoke(sessions::test_util::SimulateCommitFailed)) .WillOnce(Invoke(sessions::test_util::SimulateCommitFailed)) .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulateSuccess), - SignalEvent(&done))); - EXPECT_FALSE(GetBackoffAndResetTest(&done)); + QuitLoopNowAction())); + EXPECT_FALSE(GetBackoffAndResetTest()); } // Test that no polls or extraneous nudges occur when in backoff. -TEST_F(SyncerThreadTest, BackoffDropsJobs) { +TEST_F(SyncSchedulerTest, BackoffDropsJobs) { SyncShareRecords r; TimeDelta poll(TimeDelta::FromMilliseconds(5)); - base::WaitableEvent done(false, false); syncable::ModelTypeBitSet types; types[syncable::BOOKMARKS] = true; - syncer_thread()->OnReceivedLongPollIntervalUpdate(poll); + scheduler()->OnReceivedLongPollIntervalUpdate(poll); UseMockDelayProvider(); EXPECT_CALL(*syncer(), SyncShare(_,_,_)).Times(2) .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulateCommitFailed), - RecordSyncShareAndPostSignal(&r, 2U, this, &done))); - EXPECT_CALL(*delay(), GetDelay(_)) - .WillRepeatedly(Return(TimeDelta::FromDays(1))); + RecordSyncShareMultiple(&r, 2U))); + EXPECT_CALL(*delay(), GetDelay(_)). + WillRepeatedly(Return(TimeDelta::FromDays(1))); + + StartSyncScheduler(SyncScheduler::NORMAL_MODE); + RunLoop(); - syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL); - ASSERT_TRUE(done.TimedWait(timeout())); - done.Reset(); + // Run again to wait for polling. + RunLoop(); + + // Pump loop to get rid of nudge. + PumpLoop(); Mock::VerifyAndClearExpectations(syncer()); - EXPECT_EQ(2U, r.snapshots.size()); + ASSERT_EQ(2U, r.snapshots.size()); EXPECT_EQ(GetUpdatesCallerInfo::PERIODIC, r.snapshots[0]->source.updates_source); EXPECT_EQ(GetUpdatesCallerInfo::SYNC_CYCLE_CONTINUATION, @@ -721,43 +801,45 @@ TEST_F(SyncerThreadTest, BackoffDropsJobs) { EXPECT_CALL(*syncer(), SyncShare(_,_,_)).Times(1) .WillOnce(DoAll(Invoke(sessions::test_util::SimulateCommitFailed), - RecordSyncShareAndPostSignal(&r, 1U, this, &done))); + RecordSyncShare(&r))); // We schedule a nudge with enough delay (10X poll interval) that at least // one or two polls would have taken place. The nudge should succeed. - syncer_thread()->ScheduleNudge(poll * 10, NUDGE_SOURCE_LOCAL, types, - FROM_HERE); - ASSERT_TRUE(done.TimedWait(timeout())); - done.Reset(); + scheduler()->ScheduleNudge( + poll * 10, NUDGE_SOURCE_LOCAL, types, FROM_HERE); + RunLoop(); Mock::VerifyAndClearExpectations(syncer()); Mock::VerifyAndClearExpectations(delay()); - EXPECT_EQ(3U, r.snapshots.size()); + ASSERT_EQ(3U, r.snapshots.size()); EXPECT_EQ(GetUpdatesCallerInfo::LOCAL, r.snapshots[2]->source.updates_source); EXPECT_CALL(*syncer(), SyncShare(_,_,_)).Times(0); EXPECT_CALL(*delay(), GetDelay(_)).Times(0); - syncer_thread()->Start(SyncerThread::CONFIGURATION_MODE, NULL); - syncer_thread()->ScheduleConfig(types, - sync_api::CONFIGURE_REASON_RECONFIGURATION); - FlushLastTask(&done); + StartSyncScheduler(SyncScheduler::CONFIGURATION_MODE); + RunLoop(); + + scheduler()->ScheduleConfig( + types, sync_api::CONFIGURE_REASON_RECONFIGURATION); + PumpLoop(); + + StartSyncScheduler(SyncScheduler::NORMAL_MODE); + RunLoop(); - syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL); - syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, types, - FROM_HERE); - syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, types, - FROM_HERE); - FlushLastTask(&done); + scheduler()->ScheduleNudge( + zero(), NUDGE_SOURCE_LOCAL, types, FROM_HERE); + scheduler()->ScheduleNudge( + zero(), NUDGE_SOURCE_LOCAL, types, FROM_HERE); + PumpLoop(); } // Test that backoff is shaping traffic properly with consecutive errors. -TEST_F(SyncerThreadTest, BackoffElevation) { +TEST_F(SyncSchedulerTest, BackoffElevation) { SyncShareRecords r; const TimeDelta poll(TimeDelta::FromMilliseconds(10)); - base::WaitableEvent done(false, false); - syncer_thread()->OnReceivedLongPollIntervalUpdate(poll); + scheduler()->OnReceivedLongPollIntervalUpdate(poll); UseMockDelayProvider(); const TimeDelta first = TimeDelta::FromSeconds(1); @@ -768,7 +850,7 @@ TEST_F(SyncerThreadTest, BackoffElevation) { EXPECT_CALL(*syncer(), SyncShare(_,_,_)).Times(kMinNumSamples) .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulateCommitFailed), - RecordSyncShareAndPostSignal(&r, kMinNumSamples, this, &done))); + RecordSyncShareMultiple(&r, kMinNumSamples))); EXPECT_CALL(*delay(), GetDelay(Eq(first))).WillOnce(Return(second)) .RetiresOnSaturation(); @@ -778,9 +860,13 @@ TEST_F(SyncerThreadTest, BackoffElevation) { .RetiresOnSaturation(); EXPECT_CALL(*delay(), GetDelay(Eq(fourth))).WillOnce(Return(fifth)); - syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL); - ASSERT_TRUE(done.TimedWait(timeout())); + StartSyncScheduler(SyncScheduler::NORMAL_MODE); + RunLoop(); + // Run again to wait for polling. + RunLoop(); + + ASSERT_EQ(kMinNumSamples, r.snapshots.size()); EXPECT_GE(r.times[2] - r.times[1], second); EXPECT_GE(r.times[3] - r.times[2], third); EXPECT_GE(r.times[4] - r.times[3], fourth); @@ -788,11 +874,10 @@ TEST_F(SyncerThreadTest, BackoffElevation) { // Test that things go back to normal once a canary task makes forward progress // following a succession of failures. -TEST_F(SyncerThreadTest, BackoffRelief) { +TEST_F(SyncSchedulerTest, BackoffRelief) { SyncShareRecords r; const TimeDelta poll(TimeDelta::FromMilliseconds(10)); - base::WaitableEvent done(false, false); - syncer_thread()->OnReceivedLongPollIntervalUpdate(poll); + scheduler()->OnReceivedLongPollIntervalUpdate(poll); UseMockDelayProvider(); const TimeDelta backoff = TimeDelta::FromMilliseconds(100); @@ -801,14 +886,18 @@ TEST_F(SyncerThreadTest, BackoffRelief) { .WillOnce(Invoke(sessions::test_util::SimulateCommitFailed)) .WillOnce(Invoke(sessions::test_util::SimulateCommitFailed)) .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulateSuccess), - RecordSyncShareAndPostSignal(&r, kMinNumSamples, this, &done))); + RecordSyncShareMultiple(&r, kMinNumSamples))); EXPECT_CALL(*delay(), GetDelay(_)).WillOnce(Return(backoff)); // Optimal start for the post-backoff poll party. TimeTicks optimal_start = TimeTicks::Now() + poll + backoff; - syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL); - done.TimedWait(timeout()); - syncer_thread()->Stop(); + StartSyncScheduler(SyncScheduler::NORMAL_MODE); + RunLoop(); + + // Run again to wait for polling. + RunLoop(); + + scheduler()->Stop(); // Check for healthy polling after backoff is relieved. // Can't use AnalyzePollRun because first sync is a continuation. Bleh. @@ -822,96 +911,106 @@ TEST_F(SyncerThreadTest, BackoffRelief) { } } -TEST_F(SyncerThreadTest, GetRecommendedDelay) { +TEST_F(SyncSchedulerTest, GetRecommendedDelay) { EXPECT_LE(TimeDelta::FromSeconds(0), - SyncerThread::GetRecommendedDelay(TimeDelta::FromSeconds(0))); + SyncScheduler::GetRecommendedDelay(TimeDelta::FromSeconds(0))); EXPECT_LE(TimeDelta::FromSeconds(1), - SyncerThread::GetRecommendedDelay(TimeDelta::FromSeconds(1))); + SyncScheduler::GetRecommendedDelay(TimeDelta::FromSeconds(1))); EXPECT_LE(TimeDelta::FromSeconds(50), - SyncerThread::GetRecommendedDelay(TimeDelta::FromSeconds(50))); + SyncScheduler::GetRecommendedDelay(TimeDelta::FromSeconds(50))); EXPECT_LE(TimeDelta::FromSeconds(10), - SyncerThread::GetRecommendedDelay(TimeDelta::FromSeconds(10))); + SyncScheduler::GetRecommendedDelay(TimeDelta::FromSeconds(10))); EXPECT_EQ(TimeDelta::FromSeconds(kMaxBackoffSeconds), - SyncerThread::GetRecommendedDelay( + SyncScheduler::GetRecommendedDelay( TimeDelta::FromSeconds(kMaxBackoffSeconds))); EXPECT_EQ(TimeDelta::FromSeconds(kMaxBackoffSeconds), - SyncerThread::GetRecommendedDelay( + SyncScheduler::GetRecommendedDelay( TimeDelta::FromSeconds(kMaxBackoffSeconds + 1))); } // Test that appropriate syncer steps are requested for each job type. -TEST_F(SyncerThreadTest, SyncerSteps) { +TEST_F(SyncSchedulerTest, SyncerSteps) { // Nudges. - base::WaitableEvent done(false, false); EXPECT_CALL(*syncer(), SyncShare(_, SYNCER_BEGIN, SYNCER_END)) .Times(1); - syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL); - syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, ModelTypeBitSet(), - FROM_HERE); - FlushLastTask(&done); - syncer_thread()->Stop(); + StartSyncScheduler(SyncScheduler::NORMAL_MODE); + RunLoop(); + + scheduler()->ScheduleNudge( + zero(), NUDGE_SOURCE_LOCAL, ModelTypeBitSet(), FROM_HERE); + PumpLoop(); + // Pump again to run job. + PumpLoop(); + + scheduler()->Stop(); Mock::VerifyAndClearExpectations(syncer()); // ClearUserData. EXPECT_CALL(*syncer(), SyncShare(_, CLEAR_PRIVATE_DATA, SYNCER_END)) .Times(1); - syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL); - syncer_thread()->ScheduleClearUserData(); - FlushLastTask(&done); - syncer_thread()->Stop(); + StartSyncScheduler(SyncScheduler::NORMAL_MODE); + RunLoop(); + + scheduler()->ScheduleClearUserData(); + PumpLoop(); + PumpLoop(); + + scheduler()->Stop(); + Mock::VerifyAndClearExpectations(syncer()); // Configuration. EXPECT_CALL(*syncer(), SyncShare(_, DOWNLOAD_UPDATES, APPLY_UPDATES)); - syncer_thread()->Start(SyncerThread::CONFIGURATION_MODE, NULL); - syncer_thread()->ScheduleConfig(ModelTypeBitSet(), - sync_api::CONFIGURE_REASON_RECONFIGURATION); - FlushLastTask(&done); - syncer_thread()->Stop(); + StartSyncScheduler(SyncScheduler::CONFIGURATION_MODE); + RunLoop(); + + scheduler()->ScheduleConfig( + ModelTypeBitSet(), sync_api::CONFIGURE_REASON_RECONFIGURATION); + PumpLoop(); + PumpLoop(); + + scheduler()->Stop(); Mock::VerifyAndClearExpectations(syncer()); // Poll. EXPECT_CALL(*syncer(), SyncShare(_, SYNCER_BEGIN, SYNCER_END)) .Times(AtLeast(1)) - .WillRepeatedly(SignalEvent(&done)); + .WillRepeatedly(QuitLoopNowAction()); const TimeDelta poll(TimeDelta::FromMilliseconds(10)); - syncer_thread()->OnReceivedLongPollIntervalUpdate(poll); - syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL); - done.TimedWait(timeout()); - syncer_thread()->Stop(); + scheduler()->OnReceivedLongPollIntervalUpdate(poll); + + StartSyncScheduler(SyncScheduler::NORMAL_MODE); + RunLoop(); + + // Run again to wait for polling. + RunLoop(); + + scheduler()->Stop(); Mock::VerifyAndClearExpectations(syncer()); - done.Reset(); } // Test config tasks don't run during normal mode. // TODO(tim): Implement this test and then the functionality! -TEST_F(SyncerThreadTest, DISABLED_NoConfigDuringNormal) { +TEST_F(SyncSchedulerTest, DISABLED_NoConfigDuringNormal) { } // Test that starting the syncer thread without a valid connection doesn't // break things when a connection is detected. -TEST_F(SyncerThreadTest, StartWhenNotConnected) { - base::WaitableEvent done(false, false); - MessageLoop cur; +TEST_F(SyncSchedulerTest, StartWhenNotConnected) { connection()->SetServerNotReachable(); - EXPECT_CALL(*syncer(), SyncShare(_,_,_)).WillOnce(SignalEvent(&done)); - syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL); - syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, ModelTypeBitSet(), - FROM_HERE); - FlushLastTask(&done); + EXPECT_CALL(*syncer(), SyncShare(_,_,_)).WillOnce(QuitLoopNowAction()); + StartSyncScheduler(SyncScheduler::NORMAL_MODE); + RunLoop(); - connection()->SetServerReachable(); - cur.PostTask(FROM_HERE, NewRunnableFunction( - &SyncerThreadTest::QuitMessageLoop)); - cur.Run(); + scheduler()->ScheduleNudge( + zero(), NUDGE_SOURCE_LOCAL, ModelTypeBitSet(), FROM_HERE); + // Should save the nudge for until after the server is reachable. + PumpLoop(); - // By now, the server connection event should have been posted to the - // SyncerThread. - FlushLastTask(&done); - done.TimedWait(timeout()); + connection()->SetServerReachable(); + PumpLoop(); } -TEST_F(SyncerThreadTest, SetsPreviousRoutingInfo) { - base::WaitableEvent done(false, false); +TEST_F(SyncSchedulerTest, SetsPreviousRoutingInfo) { ModelSafeRoutingInfo info; EXPECT_TRUE(info == context()->previous_session_routing_info()); ModelSafeRoutingInfo expected; @@ -919,16 +1018,18 @@ TEST_F(SyncerThreadTest, SetsPreviousRoutingInfo) { ASSERT_FALSE(expected.empty()); EXPECT_CALL(*syncer(), SyncShare(_,_,_)).Times(1); - syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL); - syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, ModelTypeBitSet(), - FROM_HERE); - FlushLastTask(&done); - syncer_thread()->Stop(); + StartSyncScheduler(SyncScheduler::NORMAL_MODE); + RunLoop(); + + scheduler()->ScheduleNudge( + zero(), NUDGE_SOURCE_LOCAL, ModelTypeBitSet(), FROM_HERE); + PumpLoop(); + // Pump again to run job. + PumpLoop(); + + scheduler()->Stop(); EXPECT_TRUE(expected == context()->previous_session_routing_info()); } } // namespace browser_sync - -// SyncerThread won't outlive the test! -DISABLE_RUNNABLE_METHOD_REFCOUNT(browser_sync::SyncerThreadTest); diff --git a/chrome/browser/sync/engine/sync_scheduler_whitebox_unittest.cc b/chrome/browser/sync/engine/sync_scheduler_whitebox_unittest.cc new file mode 100644 index 0000000..911c5db --- /dev/null +++ b/chrome/browser/sync/engine/sync_scheduler_whitebox_unittest.cc @@ -0,0 +1,233 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/message_loop.h" +#include "base/time.h" +#include "chrome/browser/sync/engine/mock_model_safe_workers.h" +#include "chrome/browser/sync/engine/sync_scheduler.h" +#include "chrome/browser/sync/sessions/sync_session_context.h" +#include "chrome/browser/sync/sessions/test_util.h" +#include "chrome/test/sync/engine/mock_connection_manager.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::TimeDelta; +using base::TimeTicks; + +namespace browser_sync { +using sessions::SyncSessionContext; +using browser_sync::Syncer; + +class SyncSchedulerWhiteboxTest : public testing::Test { + public: + virtual void SetUp() { + syncdb_.SetUp(); + Syncer* syncer = new Syncer(); + registrar_.reset(MockModelSafeWorkerRegistrar::PassiveBookmarks()); + connection_.reset(new MockConnectionManager(syncdb_.manager(), "Test")); + connection_->SetServerReachable(); + context_ = new SyncSessionContext(connection_.get(), syncdb_.manager(), + registrar_.get(), std::vector<SyncEngineEventListener*>()); + context_->set_notifications_enabled(true); + context_->set_account_name("Test"); + scheduler_.reset( + new SyncScheduler("TestSyncSchedulerWhitebox", context_, syncer)); + } + + virtual void TearDown() { + scheduler_.reset(); + syncdb_.TearDown(); + } + + void SetMode(SyncScheduler::Mode mode) { + scheduler_->mode_ = mode; + } + + void SetLastSyncedTime(base::TimeTicks ticks) { + scheduler_->last_sync_session_end_time_ = ticks; + } + + void SetServerConnection(bool connected) { + scheduler_->server_connection_ok_ = connected; + } + + void ResetWaitInterval() { + scheduler_->wait_interval_.reset(); + } + + void SetWaitIntervalToThrottled() { + scheduler_->wait_interval_.reset(new SyncScheduler::WaitInterval( + SyncScheduler::WaitInterval::THROTTLED, TimeDelta::FromSeconds(1))); + } + + void SetWaitIntervalToExponentialBackoff() { + scheduler_->wait_interval_.reset( + new SyncScheduler::WaitInterval( + SyncScheduler::WaitInterval::EXPONENTIAL_BACKOFF, + TimeDelta::FromSeconds(1))); + } + + void SetWaitIntervalHadNudge(bool had_nudge) { + scheduler_->wait_interval_->had_nudge = had_nudge; + } + + SyncScheduler::JobProcessDecision DecideOnJob( + const SyncScheduler::SyncSessionJob& job) { + return scheduler_->DecideOnJob(job); + } + + void InitializeSyncerOnNormalMode() { + SetMode(SyncScheduler::NORMAL_MODE); + ResetWaitInterval(); + SetServerConnection(true); + SetLastSyncedTime(base::TimeTicks::Now()); + } + + SyncScheduler::JobProcessDecision CreateAndDecideJob( + SyncScheduler::SyncSessionJob::SyncSessionJobPurpose purpose) { + struct SyncScheduler::SyncSessionJob job; + job.purpose = purpose; + job.scheduled_start = TimeTicks::Now(); + return DecideOnJob(job); + } + + private: + MessageLoop message_loop_; + scoped_ptr<SyncScheduler> scheduler_; + scoped_ptr<MockConnectionManager> connection_; + SyncSessionContext* context_; + scoped_ptr<MockModelSafeWorkerRegistrar> registrar_; + MockDirectorySetterUpper syncdb_; +}; + +TEST_F(SyncSchedulerWhiteboxTest, SaveNudge) { + InitializeSyncerOnNormalMode(); + + // Now set the mode to configure. + SetMode(SyncScheduler::CONFIGURATION_MODE); + + SyncScheduler::JobProcessDecision decision = + CreateAndDecideJob(SyncScheduler::SyncSessionJob::NUDGE); + + EXPECT_EQ(decision, SyncScheduler::SAVE); +} + +TEST_F(SyncSchedulerWhiteboxTest, ContinueNudge) { + InitializeSyncerOnNormalMode(); + + SyncScheduler::JobProcessDecision decision = CreateAndDecideJob( + SyncScheduler::SyncSessionJob::NUDGE); + + EXPECT_EQ(decision, SyncScheduler::CONTINUE); +} + +TEST_F(SyncSchedulerWhiteboxTest, DropPoll) { + InitializeSyncerOnNormalMode(); + SetMode(SyncScheduler::CONFIGURATION_MODE); + + SyncScheduler::JobProcessDecision decision = CreateAndDecideJob( + SyncScheduler::SyncSessionJob::POLL); + + EXPECT_EQ(decision, SyncScheduler::DROP); +} + +TEST_F(SyncSchedulerWhiteboxTest, ContinuePoll) { + InitializeSyncerOnNormalMode(); + + SyncScheduler::JobProcessDecision decision = CreateAndDecideJob( + SyncScheduler::SyncSessionJob::POLL); + + EXPECT_EQ(decision, SyncScheduler::CONTINUE); +} + +TEST_F(SyncSchedulerWhiteboxTest, ContinueConfiguration) { + InitializeSyncerOnNormalMode(); + SetMode(SyncScheduler::CONFIGURATION_MODE); + + SyncScheduler::JobProcessDecision decision = CreateAndDecideJob( + SyncScheduler::SyncSessionJob::CONFIGURATION); + + EXPECT_EQ(decision, SyncScheduler::CONTINUE); +} + +TEST_F(SyncSchedulerWhiteboxTest, SaveConfigurationWhileThrottled) { + InitializeSyncerOnNormalMode(); + SetMode(SyncScheduler::CONFIGURATION_MODE); + + SetWaitIntervalToThrottled(); + + SyncScheduler::JobProcessDecision decision = CreateAndDecideJob( + SyncScheduler::SyncSessionJob::CONFIGURATION); + + EXPECT_EQ(decision, SyncScheduler::SAVE); +} + +TEST_F(SyncSchedulerWhiteboxTest, SaveNudgeWhileThrottled) { + InitializeSyncerOnNormalMode(); + SetMode(SyncScheduler::CONFIGURATION_MODE); + + SetWaitIntervalToThrottled(); + + SyncScheduler::JobProcessDecision decision = CreateAndDecideJob( + SyncScheduler::SyncSessionJob::NUDGE); + + EXPECT_EQ(decision, SyncScheduler::SAVE); +} + +TEST_F(SyncSchedulerWhiteboxTest, + ContinueClearUserDataUnderAllCircumstances) { + InitializeSyncerOnNormalMode(); + + SetMode(SyncScheduler::CONFIGURATION_MODE); + SetWaitIntervalToThrottled(); + SyncScheduler::JobProcessDecision decision = CreateAndDecideJob( + SyncScheduler::SyncSessionJob::CLEAR_USER_DATA); + EXPECT_EQ(decision, SyncScheduler::CONTINUE); + + SetMode(SyncScheduler::NORMAL_MODE); + SetWaitIntervalToExponentialBackoff(); + decision = CreateAndDecideJob( + SyncScheduler::SyncSessionJob::CLEAR_USER_DATA); + EXPECT_EQ(decision, SyncScheduler::CONTINUE); +} + +TEST_F(SyncSchedulerWhiteboxTest, ContinueNudgeWhileExponentialBackOff) { + InitializeSyncerOnNormalMode(); + SetMode(SyncScheduler::NORMAL_MODE); + SetWaitIntervalToExponentialBackoff(); + + SyncScheduler::JobProcessDecision decision = CreateAndDecideJob( + SyncScheduler::SyncSessionJob::NUDGE); + + EXPECT_EQ(decision, SyncScheduler::CONTINUE); +} + +TEST_F(SyncSchedulerWhiteboxTest, DropNudgeWhileExponentialBackOff) { + InitializeSyncerOnNormalMode(); + SetMode(SyncScheduler::NORMAL_MODE); + SetWaitIntervalToExponentialBackoff(); + SetWaitIntervalHadNudge(true); + + SyncScheduler::JobProcessDecision decision = CreateAndDecideJob( + SyncScheduler::SyncSessionJob::NUDGE); + + EXPECT_EQ(decision, SyncScheduler::DROP); +} + +TEST_F(SyncSchedulerWhiteboxTest, ContinueCanaryJobConfig) { + InitializeSyncerOnNormalMode(); + SetMode(SyncScheduler::CONFIGURATION_MODE); + SetWaitIntervalToExponentialBackoff(); + + struct SyncScheduler::SyncSessionJob job; + job.purpose = SyncScheduler::SyncSessionJob::CONFIGURATION; + job.scheduled_start = TimeTicks::Now(); + job.is_canary_job = true; + SyncScheduler::JobProcessDecision decision = DecideOnJob(job); + + EXPECT_EQ(decision, SyncScheduler::CONTINUE); +} + +} // namespace browser_sync diff --git a/chrome/browser/sync/engine/syncapi.cc b/chrome/browser/sync/engine/syncapi.cc index 360b694..ef3f5b4 100644 --- a/chrome/browser/sync/engine/syncapi.cc +++ b/chrome/browser/sync/engine/syncapi.cc @@ -37,8 +37,8 @@ #include "chrome/browser/sync/engine/nudge_source.h" #include "chrome/browser/sync/engine/net/server_connection_manager.h" #include "chrome/browser/sync/engine/net/syncapi_server_connection_manager.h" +#include "chrome/browser/sync/engine/sync_scheduler.h" #include "chrome/browser/sync/engine/syncer.h" -#include "chrome/browser/sync/engine/syncer_thread.h" #include "chrome/browser/sync/engine/http_post_provider_factory.h" #include "chrome/browser/sync/js_arg_list.h" #include "chrome/browser/sync/js_backend.h" @@ -86,7 +86,7 @@ using browser_sync::ServerConnectionEventListener; using browser_sync::SyncEngineEvent; using browser_sync::SyncEngineEventListener; using browser_sync::Syncer; -using browser_sync::SyncerThread; +using browser_sync::SyncScheduler; using browser_sync::kNigoriTag; using browser_sync::sessions::SyncSessionContext; using std::list; @@ -106,7 +106,7 @@ typedef GoogleServiceAuthError AuthError; static const int kThreadExitTimeoutMsec = 60000; static const int kSSLPort = 443; -static const int kSyncerThreadDelayMsec = 250; +static const int kSyncSchedulerDelayMsec = 250; #if defined(OS_CHROMEOS) static const int kChromeOSNetworkChangeReactionDelayHackMsec = 5000; @@ -1129,7 +1129,7 @@ class SyncManager::SyncInternal public: SyncInternal(const std::string& name, SyncManager* sync_manager) : name_(name), - core_message_loop_(NULL), + sync_loop_(NULL), parent_router_(NULL), sync_manager_(sync_manager), registrar_(NULL), @@ -1169,7 +1169,7 @@ class SyncManager::SyncInternal } virtual ~SyncInternal() { - CHECK(!core_message_loop_); + CHECK(!sync_loop_); DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); } @@ -1258,7 +1258,7 @@ class SyncManager::SyncInternal SyncAPIServerConnectionManager* connection_manager() { return connection_manager_.get(); } - SyncerThread* syncer_thread() { return syncer_thread_.get(); } + SyncScheduler* scheduler() { return scheduler_.get(); } UserShare* GetUserShare() { return &share_; } // Return the currently active (validated) username for use with syncable @@ -1275,6 +1275,8 @@ class SyncManager::SyncInternal browser_sync::NudgeSource source, const ModelTypeBitSet& types, const tracked_objects::Location& nudge_location); + void RequestEarlyExit(); + // See SyncManager::Shutdown for information. void Shutdown(); @@ -1539,7 +1541,7 @@ class SyncManager::SyncInternal // constructing any transaction type. UserShare share_; - MessageLoop* core_message_loop_; + MessageLoop* sync_loop_; // We have to lock around every observers_ access because it can get accessed // from any thread and added to/removed from on the core thread. @@ -1552,8 +1554,9 @@ class SyncManager::SyncInternal // client (the Syncer) and the sync server. scoped_ptr<SyncAPIServerConnectionManager> connection_manager_; - // The thread that runs the Syncer. Needs to be explicitly Start()ed. - scoped_ptr<SyncerThread> syncer_thread_; + // The scheduler that runs the Syncer. Needs to be explicitly + // Start()ed. + scoped_ptr<SyncScheduler> scheduler_; // The SyncNotifier which notifies us when updates need to be downloaded. sync_notifier::SyncNotifier* sync_notifier_; @@ -1580,22 +1583,24 @@ class SyncManager::SyncInternal // The instance is shared between the SyncManager and the Syncer. ModelSafeWorkerRegistrar* registrar_; - // Set to true once Init has been called, and we know of an authenticated - // valid) username either from a fresh authentication attempt (as in - // first-use case) or from a previous attempt stored in our UserSettings - // (as in the steady-state), and the syncable::Directory has been opened, - // meaning we are ready to accept changes. Protected by initialized_mutex_ - // as it can get read/set by both the SyncerThread and the AuthWatcherThread. + // Set to true once Init has been called, and we know of an + // authenticated valid) username either from a fresh authentication + // attempt (as in first-use case) or from a previous attempt stored + // in our UserSettings (as in the steady-state), and the + // syncable::Directory has been opened, meaning we are ready to + // accept changes. Protected by initialized_mutex_ as it can get + // read/set by both the SyncScheduler and the AuthWatcherThread. bool initialized_; mutable base::Lock initialized_mutex_; - // True if the SyncManager should be running in test mode (no syncer thread - // actually communicating with the server). + // True if the SyncManager should be running in test mode (no sync + // scheduler actually communicating with the server). bool setup_for_test_mode_; ScopedRunnableMethodFactory<SyncManager::SyncInternal> method_factory_; - // Map used to store the notification info to be displayed in about:sync page. + // Map used to store the notification info to be displayed in + // about:sync page. NotificationInfoMap notification_info_map_; browser_sync::JsDirectoryChangeListener js_directory_change_listener_; @@ -1694,30 +1699,31 @@ void SyncManager::RequestNudge(const tracked_objects::Location& location) { } void SyncManager::RequestClearServerData() { - if (data_->syncer_thread()) - data_->syncer_thread()->ScheduleClearUserData(); + if (data_->scheduler()) + data_->scheduler()->ScheduleClearUserData(); } void SyncManager::RequestConfig(const syncable::ModelTypeBitSet& types, ConfigureReason reason) { - if (!data_->syncer_thread()) { - VLOG(0) << "SyncManager::RequestConfig: bailing out because syncer thread " - << "null"; + if (!data_->scheduler()) { + LOG(INFO) + << "SyncManager::RequestConfig: bailing out because scheduler is " + << "null"; return; } StartConfigurationMode(NULL); - data_->syncer_thread()->ScheduleConfig(types, reason); + data_->scheduler()->ScheduleConfig(types, reason); } void SyncManager::StartConfigurationMode(ModeChangeCallback* callback) { - if (!data_->syncer_thread()) { - VLOG(0) << "SyncManager::StartConfigurationMode: could not start " - << "configuration mode because because syncer thread is not " - << "created"; + if (!data_->scheduler()) { + LOG(INFO) + << "SyncManager::StartConfigurationMode: could not start " + << "configuration mode because because scheduler is null"; return; } - data_->syncer_thread()->Start( - browser_sync::SyncerThread::CONFIGURATION_MODE, callback); + data_->scheduler()->Start( + browser_sync::SyncScheduler::CONFIGURATION_MODE, callback); } const std::string& SyncManager::GetAuthenticatedUsername() { @@ -1740,8 +1746,8 @@ bool SyncManager::SyncInternal::Init( VLOG(1) << "Starting SyncInternal initialization."; - core_message_loop_ = MessageLoop::current(); - DCHECK(core_message_loop_); + sync_loop_ = MessageLoop::current(); + DCHECK(sync_loop_); registrar_ = model_safe_worker_registrar; setup_for_test_mode_ = setup_for_test_mode; @@ -1759,7 +1765,7 @@ bool SyncManager::SyncInternal::Init( // TODO(akalin): CheckServerReachable() can block, which may cause jank if we // try to shut down sync. Fix this. - core_message_loop_->PostTask(FROM_HERE, + sync_loop_->PostTask(FROM_HERE, method_factory_.NewRunnableMethod(&SyncInternal::CheckServerReachable)); // Test mode does not use a syncer context or syncer thread. @@ -1775,16 +1781,15 @@ bool SyncManager::SyncInternal::Init( model_safe_worker_registrar, listeners); context->set_account_name(credentials.email); - // The SyncerThread takes ownership of |context|. - syncer_thread_.reset( - new SyncerThread(name_, context, new Syncer())); + // The SyncScheduler takes ownership of |context|. + scheduler_.reset(new SyncScheduler(name_, context, new Syncer())); } bool signed_in = SignIn(credentials); - if (signed_in && syncer_thread()) { - syncer_thread()->Start( - browser_sync::SyncerThread::CONFIGURATION_MODE, NULL); + if (signed_in && scheduler()) { + scheduler()->Start( + browser_sync::SyncScheduler::CONFIGURATION_MODE, NULL); } // Do this once the directory is opened. @@ -1797,7 +1802,7 @@ void SyncManager::SyncInternal::BootstrapEncryption( const std::string& restored_key_for_bootstrapping) { syncable::ScopedDirLookup lookup(dir_manager(), username_for_share()); if (!lookup.good()) { - VLOG(0) << "BootstrapEncryption: lookup not good so bailing out"; + LOG(INFO) << "BootstrapEncryption: lookup not good so bailing out"; NOTREACHED(); return; } @@ -1842,12 +1847,11 @@ void SyncManager::SyncInternal::BootstrapEncryption( } void SyncManager::SyncInternal::StartSyncingNormally() { - // Start the syncer thread. This won't actually - // result in any syncing until at least the - // DirectoryManager broadcasts the OPENED event, - // and a valid server connection is detected. - if (syncer_thread()) // NULL during certain unittests. - syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL); + // Start the sync scheduler. This won't actually result in any + // syncing until at least the DirectoryManager broadcasts the OPENED + // event, and a valid server connection is detected. + if (scheduler()) // NULL during certain unittests. + scheduler()->Start(SyncScheduler::NORMAL_MODE, NULL); } void SyncManager::SyncInternal::MarkAndNotifyInitializationComplete() { @@ -1871,7 +1875,7 @@ void SyncManager::SyncInternal::MarkAndNotifyInitializationComplete() { } void SyncManager::SyncInternal::SendNotification() { - DCHECK_EQ(MessageLoop::current(), core_message_loop_); + DCHECK_EQ(MessageLoop::current(), sync_loop_); if (!sync_notifier_) { VLOG(1) << "Not sending notification: sync_notifier_ is NULL"; return; @@ -1916,7 +1920,7 @@ bool SyncManager::SyncInternal::OpenDirectory() { } bool SyncManager::SyncInternal::SignIn(const SyncCredentials& credentials) { - DCHECK_EQ(MessageLoop::current(), core_message_loop_); + DCHECK_EQ(MessageLoop::current(), sync_loop_); DCHECK(share_.name.empty()); share_.name = credentials.email; @@ -1947,7 +1951,7 @@ bool SyncManager::SyncInternal::SignIn(const SyncCredentials& credentials) { void SyncManager::SyncInternal::UpdateCredentials( const SyncCredentials& credentials) { - DCHECK_EQ(MessageLoop::current(), core_message_loop_); + DCHECK_EQ(MessageLoop::current(), sync_loop_); DCHECK_EQ(credentials.email, share_.name); DCHECK(!credentials.email.empty()); DCHECK(!credentials.sync_token.empty()); @@ -1960,7 +1964,7 @@ void SyncManager::SyncInternal::UpdateCredentials( } void SyncManager::SyncInternal::UpdateEnabledTypes() { - DCHECK_EQ(MessageLoop::current(), core_message_loop_); + DCHECK_EQ(MessageLoop::current(), sync_loop_); ModelSafeRoutingInfo routes; registrar_->GetModelSafeRoutingInfo(&routes); syncable::ModelTypeSet enabled_types; @@ -2202,6 +2206,16 @@ browser_sync::JsBackend* SyncManager::GetJsBackend() { return data_; } +void SyncManager::RequestEarlyExit() { + data_->RequestEarlyExit(); +} + +void SyncManager::SyncInternal::RequestEarlyExit() { + if (scheduler()) { + scheduler()->RequestEarlyExit(); + } +} + void SyncManager::Shutdown() { data_->Shutdown(); } @@ -2209,10 +2223,8 @@ void SyncManager::Shutdown() { void SyncManager::SyncInternal::Shutdown() { method_factory_.RevokeAll(); - if (syncer_thread()) { - syncer_thread()->Stop(); - syncer_thread_.reset(); - } + // Automatically stops the scheduler. + scheduler_.reset(); // We NULL out sync_notifer_ so that any pending tasks do not // trigger further notifications. @@ -2221,18 +2233,17 @@ void SyncManager::SyncInternal::Shutdown() { sync_notifier_->RemoveObserver(this); } - // |this| is about to be destroyed, so we have to ensure any messages - // that were posted to core_thread_ before or during syncer thread shutdown - // are flushed out, else they refer to garbage memory. SendNotification - // is an example. - // TODO(tim): Remove this monstrosity, perhaps with ObserverListTS once core - // thread is removed. Bug 78190. + // |this| is about to be destroyed, so we have to ensure any + // messages that were posted to sync_thread_ are flushed out, else + // they refer to garbage memory. SendNotification is an example. + // TODO(akalin): Remove this monstrosity, perhaps with + // ObserverListTS once core thread is removed. Bug 78190. { - CHECK(core_message_loop_); - bool old_state = core_message_loop_->NestableTasksAllowed(); - core_message_loop_->SetNestableTasksAllowed(true); - core_message_loop_->RunAllPending(); - core_message_loop_->SetNestableTasksAllowed(old_state); + CHECK(sync_loop_); + bool old_state = sync_loop_->NestableTasksAllowed(); + sync_loop_->SetNestableTasksAllowed(true); + sync_loop_->RunAllPending(); + sync_loop_->SetNestableTasksAllowed(old_state); } net::NetworkChangeNotifier::RemoveIPAddressObserver(this); @@ -2248,7 +2259,7 @@ void SyncManager::SyncInternal::Shutdown() { // handles to backing files. share_.dir_manager.reset(); - core_message_loop_ = NULL; + sync_loop_ = NULL; } void SyncManager::SyncInternal::OnIPAddressChanged() { @@ -2379,10 +2390,10 @@ void SyncManager::SyncInternal::HandleCalculateChangesChangeEventFromSyncApi( only_preference_changes = false; } } - if (exists_unsynced_items && syncer_thread()) { + if (exists_unsynced_items && scheduler()) { int nudge_delay = only_preference_changes ? kPreferencesNudgeDelayMilliseconds : kDefaultNudgeDelayMilliseconds; - core_message_loop_->PostTask(FROM_HERE, + sync_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, &SyncInternal::RequestNudgeWithDataTypes, TimeDelta::FromMilliseconds(nudge_delay), browser_sync::NUDGE_SOURCE_LOCAL, @@ -2466,8 +2477,8 @@ SyncManager::Status SyncManager::SyncInternal::GetStatus() { void SyncManager::SyncInternal::RequestNudge( const tracked_objects::Location& location) { - if (syncer_thread()) - syncer_thread()->ScheduleNudge( + if (scheduler()) + scheduler()->ScheduleNudge( TimeDelta::FromMilliseconds(0), browser_sync::NUDGE_SOURCE_LOCAL, ModelTypeBitSet(), location); } @@ -2476,14 +2487,15 @@ void SyncManager::SyncInternal::RequestNudgeWithDataTypes( const TimeDelta& delay, browser_sync::NudgeSource source, const ModelTypeBitSet& types, const tracked_objects::Location& nudge_location) { - if (syncer_thread()) - syncer_thread()->ScheduleNudge(delay, source, types, nudge_location); + if (scheduler()) + scheduler()->ScheduleNudge(delay, source, types, nudge_location); } void SyncManager::SyncInternal::OnSyncEngineEvent( const SyncEngineEvent& event) { if (!HaveObservers()) { - VLOG(0) << "OnSyncEngineEvent returning because observers_.size() is zero"; + LOG(INFO) + << "OnSyncEngineEvent returning because observers_.size() is zero"; return; } @@ -2550,8 +2562,8 @@ void SyncManager::SyncInternal::OnSyncEngineEvent( } if (!initialized()) { - VLOG(0) << "OnSyncCycleCompleted not sent because sync api is not " - << "initialized"; + LOG(INFO) << "OnSyncCycleCompleted not sent because sync api is not " + << "initialized"; return; } @@ -2572,7 +2584,7 @@ void SyncManager::SyncInternal::OnSyncEngineEvent( (event.snapshot->syncer_status.num_successful_commits > 0); if (is_notifiable_commit) { allstatus_.IncrementNotifiableCommits(); - core_message_loop_->PostTask( + sync_loop_->PostTask( FROM_HERE, NewRunnableMethod( this, @@ -2842,8 +2854,8 @@ void SyncManager::SyncInternal::OnNotificationStateChange( VLOG(1) << "P2P: Notifications enabled = " << (notifications_enabled ? "true" : "false"); allstatus_.SetNotificationsEnabled(notifications_enabled); - if (syncer_thread()) { - syncer_thread()->set_notifications_enabled(notifications_enabled); + if (scheduler()) { + scheduler()->set_notifications_enabled(notifications_enabled); } if (parent_router_) { DictionaryValue details; @@ -2866,9 +2878,9 @@ void SyncManager::SyncInternal::UpdateNotificationInfo( void SyncManager::SyncInternal::OnIncomingNotification( const syncable::ModelTypePayloadMap& type_payloads) { if (!type_payloads.empty()) { - if (syncer_thread()) { - syncer_thread()->ScheduleNudgeWithPayloads( - TimeDelta::FromMilliseconds(kSyncerThreadDelayMsec), + if (scheduler()) { + scheduler()->ScheduleNudgeWithPayloads( + TimeDelta::FromMilliseconds(kSyncSchedulerDelayMsec), browser_sync::NUDGE_SOURCE_NOTIFICATION, type_payloads, FROM_HERE); } diff --git a/chrome/browser/sync/engine/syncapi.h b/chrome/browser/sync/engine/syncapi.h index 2db4020..ce18184 100644 --- a/chrome/browser/sync/engine/syncapi.h +++ b/chrome/browser/sync/engine/syncapi.h @@ -912,15 +912,14 @@ class SyncManager { // types, as we do not currently support decrypting datatypes. void EncryptDataTypes(const syncable::ModelTypeSet& encrypted_types); - // Puts the SyncerThread into a mode where no normal nudge or poll traffic + // Puts the SyncScheduler into a mode where no normal nudge or poll traffic // will occur, but calls to RequestConfig will be supported. If |callback| - // is provided, it will be invoked (from the internal SyncerThread) when + // is provided, it will be invoked (from the internal SyncScheduler) when // the thread has changed to configuration mode. void StartConfigurationMode(ModeChangeCallback* callback); - // For the new SyncerThread impl, this switches the mode of operation to - // CONFIGURATION_MODE and schedules a config task to fetch updates for - // |types|. + // Switches the mode of operation to CONFIGURATION_MODE and + // schedules a config task to fetch updates for |types|. void RequestConfig(const syncable::ModelTypeBitSet& types, sync_api::ConfigureReason reason); @@ -1027,6 +1026,8 @@ class SyncManager { // to the syncapi model. void SaveChanges(); + void RequestEarlyExit(); + // Issue a final SaveChanges, close sqlite handles, and stop running threads. // Must be called from the same thread that called Init(). void Shutdown(); diff --git a/chrome/browser/sync/engine/syncer_thread_whitebox_unittest.cc b/chrome/browser/sync/engine/syncer_thread_whitebox_unittest.cc deleted file mode 100644 index 681474a..0000000 --- a/chrome/browser/sync/engine/syncer_thread_whitebox_unittest.cc +++ /dev/null @@ -1,232 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/time.h" -#include "chrome/browser/sync/engine/mock_model_safe_workers.h" -#include "chrome/browser/sync/engine/syncer_thread.h" -#include "chrome/browser/sync/sessions/sync_session_context.h" -#include "chrome/browser/sync/sessions/test_util.h" -#include "chrome/test/sync/engine/mock_connection_manager.h" -#include "chrome/test/sync/engine/test_directory_setter_upper.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/gmock/include/gmock/gmock.h" - -using base::TimeDelta; -using base::TimeTicks; - -namespace browser_sync { -using sessions::SyncSessionContext; -using browser_sync::Syncer; - -class SyncerThreadWhiteboxTest : public testing::Test { - public: - virtual void SetUp() { - syncdb_.SetUp(); - Syncer* syncer = new Syncer(); - registrar_.reset(MockModelSafeWorkerRegistrar::PassiveBookmarks()); - context_ = new SyncSessionContext(connection_.get(), syncdb_.manager(), - registrar_.get(), std::vector<SyncEngineEventListener*>()); - context_->set_notifications_enabled(true); - context_->set_account_name("Test"); - syncer_thread_.reset( - new SyncerThread("TestSyncerThreadWhitebox", context_, syncer)); - } - - virtual void TearDown() { - syncdb_.TearDown(); - } - - void SetMode(SyncerThread::Mode mode) { - syncer_thread_->mode_ = mode; - } - - void SetLastSyncedTime(base::TimeTicks ticks) { - syncer_thread_->last_sync_session_end_time_ = ticks; - } - - void SetServerConnection(bool connected) { - syncer_thread_->server_connection_ok_ = connected; - } - - void ResetWaitInterval() { - syncer_thread_->wait_interval_.reset(); - } - - void SetWaitIntervalToThrottled() { - syncer_thread_->wait_interval_.reset(new SyncerThread::WaitInterval( - SyncerThread::WaitInterval::THROTTLED, TimeDelta::FromSeconds(1))); - } - - void SetWaitIntervalToExponentialBackoff() { - syncer_thread_->wait_interval_.reset( - new SyncerThread::WaitInterval( - SyncerThread::WaitInterval::EXPONENTIAL_BACKOFF, - TimeDelta::FromSeconds(1))); - } - - SyncerThread::JobProcessDecision DecideOnJob( - const SyncerThread::SyncSessionJob& job) { - return syncer_thread_->DecideOnJob(job); - } - - void InitializeSyncerOnNormalMode() { - SetMode(SyncerThread::NORMAL_MODE); - ResetWaitInterval(); - SetServerConnection(true); - SetLastSyncedTime(base::TimeTicks::Now()); - } - - SyncerThread::JobProcessDecision CreateAndDecideJob( - SyncerThread::SyncSessionJob::SyncSessionJobPurpose purpose) { - struct SyncerThread::SyncSessionJob job; - job.purpose = purpose; - job.scheduled_start = TimeTicks::Now(); - return DecideOnJob(job); - } - - protected: - scoped_ptr<SyncerThread> syncer_thread_; - - private: - scoped_ptr<MockConnectionManager> connection_; - SyncSessionContext* context_; - //MockDelayProvider* delay_; - scoped_ptr<MockModelSafeWorkerRegistrar> registrar_; - MockDirectorySetterUpper syncdb_; -}; - -TEST_F(SyncerThreadWhiteboxTest, SaveNudge) { - InitializeSyncerOnNormalMode(); - - // Now set the mode to configure. - SetMode(SyncerThread::CONFIGURATION_MODE); - - SyncerThread::JobProcessDecision decision = - CreateAndDecideJob(SyncerThread::SyncSessionJob::NUDGE); - - EXPECT_EQ(decision, SyncerThread::SAVE); -} - -TEST_F(SyncerThreadWhiteboxTest, ContinueNudge) { - InitializeSyncerOnNormalMode(); - - SyncerThread::JobProcessDecision decision = CreateAndDecideJob( - SyncerThread::SyncSessionJob::NUDGE); - - EXPECT_EQ(decision, SyncerThread::CONTINUE); -} - -TEST_F(SyncerThreadWhiteboxTest, DropPoll) { - InitializeSyncerOnNormalMode(); - SetMode(SyncerThread::CONFIGURATION_MODE); - - SyncerThread::JobProcessDecision decision = CreateAndDecideJob( - SyncerThread::SyncSessionJob::POLL); - - EXPECT_EQ(decision, SyncerThread::DROP); -} - -TEST_F(SyncerThreadWhiteboxTest, ContinuePoll) { - InitializeSyncerOnNormalMode(); - - SyncerThread::JobProcessDecision decision = CreateAndDecideJob( - SyncerThread::SyncSessionJob::POLL); - - EXPECT_EQ(decision, SyncerThread::CONTINUE); -} - -TEST_F(SyncerThreadWhiteboxTest, ContinueConfiguration) { - InitializeSyncerOnNormalMode(); - SetMode(SyncerThread::CONFIGURATION_MODE); - - SyncerThread::JobProcessDecision decision = CreateAndDecideJob( - SyncerThread::SyncSessionJob::CONFIGURATION); - - EXPECT_EQ(decision, SyncerThread::CONTINUE); -} - -TEST_F(SyncerThreadWhiteboxTest, SaveConfigurationWhileThrottled) { - InitializeSyncerOnNormalMode(); - SetMode(SyncerThread::CONFIGURATION_MODE); - - SetWaitIntervalToThrottled(); - - SyncerThread::JobProcessDecision decision = CreateAndDecideJob( - SyncerThread::SyncSessionJob::CONFIGURATION); - - EXPECT_EQ(decision, SyncerThread::SAVE); -} - -TEST_F(SyncerThreadWhiteboxTest, SaveNudgeWhileThrottled) { - InitializeSyncerOnNormalMode(); - SetMode(SyncerThread::CONFIGURATION_MODE); - - SetWaitIntervalToThrottled(); - - SyncerThread::JobProcessDecision decision = CreateAndDecideJob( - SyncerThread::SyncSessionJob::NUDGE); - - EXPECT_EQ(decision, SyncerThread::SAVE); - -} - -TEST_F(SyncerThreadWhiteboxTest, ContinueClearUserDataUnderAllCircumstances) { - InitializeSyncerOnNormalMode(); - - SetMode(SyncerThread::CONFIGURATION_MODE); - SetWaitIntervalToThrottled(); - SyncerThread::JobProcessDecision decision = CreateAndDecideJob( - SyncerThread::SyncSessionJob::CLEAR_USER_DATA); - EXPECT_EQ(decision, SyncerThread::CONTINUE); - - SetMode(SyncerThread::NORMAL_MODE); - SetWaitIntervalToExponentialBackoff(); - decision = CreateAndDecideJob( - SyncerThread::SyncSessionJob::CLEAR_USER_DATA); - EXPECT_EQ(decision, SyncerThread::CONTINUE); -} - -TEST_F(SyncerThreadWhiteboxTest, ContinueNudgeWhileExponentialBackOff) { - InitializeSyncerOnNormalMode(); - SetMode(SyncerThread::NORMAL_MODE); - SetWaitIntervalToExponentialBackoff(); - - SyncerThread::JobProcessDecision decision = CreateAndDecideJob( - SyncerThread::SyncSessionJob::NUDGE); - - EXPECT_EQ(decision, SyncerThread::CONTINUE); -} - -TEST_F(SyncerThreadWhiteboxTest, DropNudgeWhileExponentialBackOff) { - InitializeSyncerOnNormalMode(); - SetMode(SyncerThread::NORMAL_MODE); - SetWaitIntervalToExponentialBackoff(); - - syncer_thread_->wait_interval_->had_nudge = true; - - SyncerThread::JobProcessDecision decision = CreateAndDecideJob( - SyncerThread::SyncSessionJob::NUDGE); - - EXPECT_EQ(decision, SyncerThread::DROP); -} - -TEST_F(SyncerThreadWhiteboxTest, ContinueCanaryJobConfig) { - InitializeSyncerOnNormalMode(); - SetMode(SyncerThread::CONFIGURATION_MODE); - SetWaitIntervalToExponentialBackoff(); - - struct SyncerThread::SyncSessionJob job; - job.purpose = SyncerThread::SyncSessionJob::CONFIGURATION; - job.scheduled_start = TimeTicks::Now(); - job.is_canary_job = true; - SyncerThread::JobProcessDecision decision = DecideOnJob(job); - - EXPECT_EQ(decision, SyncerThread::CONTINUE); -} - -} // namespace browser_sync - -// SyncerThread won't outlive the test! -DISABLE_RUNNABLE_METHOD_REFCOUNT( - browser_sync::SyncerThreadWhiteboxTest); diff --git a/chrome/browser/sync/glue/sync_backend_host.cc b/chrome/browser/sync/glue/sync_backend_host.cc index 931721b..9316961 100644 --- a/chrome/browser/sync/glue/sync_backend_host.cc +++ b/chrome/browser/sync/glue/sync_backend_host.cc @@ -61,7 +61,7 @@ using sync_api::SyncCredentials; SyncBackendHost::SyncBackendHost(Profile* profile) : core_(new Core(profile->GetDebugName(), ALLOW_THIS_IN_INITIALIZER_LIST(this))), - core_thread_("Chrome_SyncCoreThread"), + sync_thread_("Chrome_SyncThread"), frontend_loop_(MessageLoop::current()), profile_(profile), frontend_(NULL), @@ -72,7 +72,7 @@ SyncBackendHost::SyncBackendHost(Profile* profile) } SyncBackendHost::SyncBackendHost() - : core_thread_("Chrome_SyncCoreThread"), + : sync_thread_("Chrome_SyncThread"), frontend_loop_(MessageLoop::current()), profile_(NULL), frontend_(NULL), @@ -92,7 +92,7 @@ void SyncBackendHost::Initialize( net::URLRequestContextGetter* baseline_context_getter, const SyncCredentials& credentials, bool delete_sync_data_folder) { - if (!core_thread_.Start()) + if (!sync_thread_.Start()) return; frontend_ = frontend; @@ -191,13 +191,13 @@ sync_api::HttpPostProviderFactory* SyncBackendHost::MakeHttpBridgeFactory( } void SyncBackendHost::InitCore(const Core::DoInitializeOptions& options) { - core_thread_.message_loop()->PostTask(FROM_HERE, + sync_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoInitialize, options)); } void SyncBackendHost::UpdateCredentials(const SyncCredentials& credentials) { - core_thread_.message_loop()->PostTask(FROM_HERE, + sync_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoUpdateCredentials, credentials)); @@ -205,7 +205,7 @@ void SyncBackendHost::UpdateCredentials(const SyncCredentials& credentials) { void SyncBackendHost::StartSyncingWithServer() { VLOG(1) << "SyncBackendHost::StartSyncingWithServer called."; - core_thread_.message_loop()->PostTask(FROM_HERE, + sync_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoStartSyncing)); } @@ -226,24 +226,25 @@ void SyncBackendHost::SetPassphrase(const std::string& passphrase, core_->set_processing_passphrase(); // If encryption is enabled and we've got a SetPassphrase - core_thread_.message_loop()->PostTask(FROM_HERE, + sync_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoSetPassphrase, passphrase, is_explicit)); } void SyncBackendHost::Shutdown(bool sync_disabled) { // Thread shutdown should occur in the following order: - // - SyncerThread - // - CoreThread + // - Sync Thread // - UI Thread (stops some time after we return from this call). - if (core_thread_.IsRunning()) { // Not running in tests. - core_thread_.message_loop()->PostTask(FROM_HERE, + if (sync_thread_.IsRunning()) { // Not running in tests. + // TODO(akalin): Remove the need for this. + core_->syncapi()->RequestEarlyExit(); + sync_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoShutdown, sync_disabled)); } - // Before joining the core_thread_, we wait for the UIModelWorker to + // Before joining the sync_thread_, we wait for the UIModelWorker to // give us the green light that it is not depending on the frontend_loop_ to // process any more tasks. Stop() blocks until this termination condition // is true. @@ -251,7 +252,7 @@ void SyncBackendHost::Shutdown(bool sync_disabled) { ui_worker()->Stop(); // Stop will return once the thread exits, which will be after DoShutdown - // runs. DoShutdown needs to run from core_thread_ because the sync backend + // runs. DoShutdown needs to run from sync_thread_ because the sync backend // requires any thread that opened sqlite handles to relinquish them // personally. We need to join threads, because otherwise the main Chrome // thread (ui loop) can exit before DoShutdown finishes, at which point @@ -263,7 +264,7 @@ void SyncBackendHost::Shutdown(bool sync_disabled) { // this, see bug 19757. { base::ThreadRestrictions::ScopedAllowIO allow_io; - core_thread_.Stop(); + sync_thread_.Stop(); } registrar_.routing_info.clear(); @@ -427,8 +428,8 @@ void SyncBackendHost::StartConfiguration(Callback0::Type* callback) { // Put syncer in the config mode. DTM will put us in normal mode once it is. // done. This is to ensure we dont do a normal sync when we are doing model // association. - core_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod( - core_.get(),&SyncBackendHost::Core::DoStartConfiguration, callback)); + sync_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod( + core_.get(), &SyncBackendHost::Core::DoStartConfiguration, callback)); } void SyncBackendHost::FinishConfigureDataTypesOnFrontendLoop() { @@ -447,7 +448,7 @@ void SyncBackendHost::FinishConfigureDataTypesOnFrontendLoop() { VLOG(1) << "Syncer in config mode. SBH executing" << "FinishConfigureDataTypesOnFrontendLoop"; if (pending_config_mode_state_->deleted_type) { - core_thread_.message_loop()->PostTask(FROM_HERE, + sync_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DeferNudgeForCleanup)); } @@ -480,7 +481,7 @@ void SyncBackendHost::FinishConfigureDataTypesOnFrontendLoop() { types_copy.set(syncable::NIGORI); VLOG(1) << "SyncBackendHost(" << this << "):New Types added. " << "Calling DoRequestConfig"; - core_thread_.message_loop()->PostTask(FROM_HERE, + sync_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoRequestConfig, types_copy, @@ -490,14 +491,14 @@ void SyncBackendHost::FinishConfigureDataTypesOnFrontendLoop() { pending_config_mode_state_.reset(); // Notify the SyncManager about the new types. - core_thread_.message_loop()->PostTask(FROM_HERE, + sync_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoUpdateEnabledTypes)); } void SyncBackendHost::EncryptDataTypes( const syncable::ModelTypeSet& encrypted_types) { - core_thread_.message_loop()->PostTask(FROM_HERE, + sync_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoEncryptDataTypes, encrypted_types)); @@ -509,7 +510,7 @@ syncable::ModelTypeSet SyncBackendHost::GetEncryptedDataTypes() const { } void SyncBackendHost::RequestNudge(const tracked_objects::Location& location) { - core_thread_.message_loop()->PostTask(FROM_HERE, + sync_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoRequestNudge, location)); } @@ -551,7 +552,7 @@ void SyncBackendHost::DeactivateDataType( } bool SyncBackendHost::RequestClearServerData() { - core_thread_.message_loop()->PostTask(FROM_HERE, + sync_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoRequestClearServerData)); return true; @@ -740,7 +741,7 @@ std::string MakeUserAgentForSyncapi() { } void SyncBackendHost::Core::DoInitialize(const DoInitializeOptions& options) { - DCHECK(MessageLoop::current() == host_->core_thread_.message_loop()); + DCHECK(MessageLoop::current() == host_->sync_thread_.message_loop()); processing_passphrase_ = false; // Blow away the partial or corrupt sync data folder before doing any more @@ -773,17 +774,17 @@ void SyncBackendHost::Core::DoInitialize(const DoInitializeOptions& options) { void SyncBackendHost::Core::DoUpdateCredentials( const SyncCredentials& credentials) { - DCHECK(MessageLoop::current() == host_->core_thread_.message_loop()); + DCHECK(MessageLoop::current() == host_->sync_thread_.message_loop()); syncapi_->UpdateCredentials(credentials); } void SyncBackendHost::Core::DoUpdateEnabledTypes() { - DCHECK(MessageLoop::current() == host_->core_thread_.message_loop()); + DCHECK(MessageLoop::current() == host_->sync_thread_.message_loop()); syncapi_->UpdateEnabledTypes(); } void SyncBackendHost::Core::DoStartSyncing() { - DCHECK(MessageLoop::current() == host_->core_thread_.message_loop()); + DCHECK(MessageLoop::current() == host_->sync_thread_.message_loop()); syncapi_->StartSyncingNormally(); if (deferred_nudge_for_cleanup_requested_) syncapi_->RequestNudge(FROM_HERE); @@ -792,7 +793,7 @@ void SyncBackendHost::Core::DoStartSyncing() { void SyncBackendHost::Core::DoSetPassphrase(const std::string& passphrase, bool is_explicit) { - DCHECK(MessageLoop::current() == host_->core_thread_.message_loop()); + DCHECK(MessageLoop::current() == host_->sync_thread_.message_loop()); syncapi_->SetPassphrase(passphrase, is_explicit); } @@ -808,7 +809,7 @@ void SyncBackendHost::Core::set_processing_passphrase() { void SyncBackendHost::Core::DoEncryptDataTypes( const syncable::ModelTypeSet& encrypted_types) { - DCHECK(MessageLoop::current() == host_->core_thread_.message_loop()); + DCHECK(MessageLoop::current() == host_->sync_thread_.message_loop()); syncapi_->EncryptDataTypes(encrypted_types); } @@ -832,7 +833,7 @@ UIModelWorker* SyncBackendHost::ui_worker() { } void SyncBackendHost::Core::DoShutdown(bool sync_disabled) { - DCHECK(MessageLoop::current() == host_->core_thread_.message_loop()); + DCHECK(MessageLoop::current() == host_->sync_thread_.message_loop()); save_changes_timer_.Stop(); syncapi_->Shutdown(); // Stops the SyncerThread. @@ -964,7 +965,7 @@ void SyncBackendHost::Core::OnInitializationComplete() { &Core::HandleInitalizationCompletedOnFrontendLoop)); // Initialization is complete, so we can schedule recurring SaveChanges. - host_->core_thread_.message_loop()->PostTask(FROM_HERE, + host_->sync_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this, &Core::StartSavingChanges)); } @@ -1141,7 +1142,7 @@ void SyncBackendHost::Core::SetParentJsEventRouter(JsEventRouter* router) { DCHECK_EQ(MessageLoop::current(), host_->frontend_loop_); DCHECK(router); parent_router_ = router; - MessageLoop* core_message_loop = host_->core_thread_.message_loop(); + MessageLoop* core_message_loop = host_->sync_thread_.message_loop(); CHECK(core_message_loop); core_message_loop->PostTask( FROM_HERE, @@ -1152,7 +1153,7 @@ void SyncBackendHost::Core::SetParentJsEventRouter(JsEventRouter* router) { void SyncBackendHost::Core::RemoveParentJsEventRouter() { DCHECK_EQ(MessageLoop::current(), host_->frontend_loop_); parent_router_ = NULL; - MessageLoop* core_message_loop = host_->core_thread_.message_loop(); + MessageLoop* core_message_loop = host_->sync_thread_.message_loop(); CHECK(core_message_loop); core_message_loop->PostTask( FROM_HERE, @@ -1169,7 +1170,7 @@ void SyncBackendHost::Core::ProcessMessage( const std::string& name, const JsArgList& args, const JsEventHandler* sender) { DCHECK_EQ(MessageLoop::current(), host_->frontend_loop_); - MessageLoop* core_message_loop = host_->core_thread_.message_loop(); + MessageLoop* core_message_loop = host_->sync_thread_.message_loop(); CHECK(core_message_loop); core_message_loop->PostTask( FROM_HERE, @@ -1179,7 +1180,7 @@ void SyncBackendHost::Core::ProcessMessage( } void SyncBackendHost::Core::ConnectChildJsEventRouter() { - DCHECK_EQ(MessageLoop::current(), host_->core_thread_.message_loop()); + DCHECK_EQ(MessageLoop::current(), host_->sync_thread_.message_loop()); // We need this check since AddObserver() can be called at most once // for a given observer. if (!syncapi_->GetJsBackend()->GetParentJsEventRouter()) { @@ -1189,7 +1190,7 @@ void SyncBackendHost::Core::ConnectChildJsEventRouter() { } void SyncBackendHost::Core::DisconnectChildJsEventRouter() { - DCHECK_EQ(MessageLoop::current(), host_->core_thread_.message_loop()); + DCHECK_EQ(MessageLoop::current(), host_->sync_thread_.message_loop()); syncapi_->GetJsBackend()->RemoveParentJsEventRouter(); syncapi_->RemoveObserver(&sync_manager_observer_); } @@ -1197,12 +1198,12 @@ void SyncBackendHost::Core::DisconnectChildJsEventRouter() { void SyncBackendHost::Core::DoProcessMessage( const std::string& name, const JsArgList& args, const JsEventHandler* sender) { - DCHECK_EQ(MessageLoop::current(), host_->core_thread_.message_loop()); + DCHECK_EQ(MessageLoop::current(), host_->sync_thread_.message_loop()); syncapi_->GetJsBackend()->ProcessMessage(name, args, sender); } void SyncBackendHost::Core::DeferNudgeForCleanup() { - DCHECK_EQ(MessageLoop::current(), host_->core_thread_.message_loop()); + DCHECK_EQ(MessageLoop::current(), host_->sync_thread_.message_loop()); deferred_nudge_for_cleanup_requested_ = true; } diff --git a/chrome/browser/sync/glue/sync_backend_host.h b/chrome/browser/sync/glue/sync_backend_host.h index 84b0537..d9c0ae5 100644 --- a/chrome/browser/sync/glue/sync_backend_host.h +++ b/chrome/browser/sync/glue/sync_backend_host.h @@ -178,7 +178,7 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar { // Encrypts the specified datatypes and marks them as needing encryption on // other machines. This affects all machines synced to this account and all // data belonging to the specified types. - // Note: actual work is done on core_thread_'s message loop. + // Note: actual work is done on sync_thread_'s message loop. virtual void EncryptDataTypes( const syncable::ModelTypeSet& encrypted_types); @@ -338,22 +338,22 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar { // It calls us on a dedicated thread to actually perform synchronous // (and potentially blocking) syncapi operations. // - // Called on the SyncBackendHost core_thread_ to perform initialization + // Called on the SyncBackendHost sync_thread_ to perform initialization // of the syncapi on behalf of SyncBackendHost::Initialize. void DoInitialize(const DoInitializeOptions& options); - // Called on our SyncBackendHost's core_thread_ to perform credential + // Called on our SyncBackendHost's sync_thread_ to perform credential // update on behalf of SyncBackendHost::UpdateCredentials void DoUpdateCredentials(const sync_api::SyncCredentials& credentials); // Called when the user disables or enables a sync type. void DoUpdateEnabledTypes(); - // Called on the SyncBackendHost core_thread_ to tell the syncapi to start + // Called on the SyncBackendHost sync_thread_ to tell the syncapi to start // syncing (generally after initialization and authentication). void DoStartSyncing(); - // Called on the SyncBackendHost core_thread_ to nudge/pause/resume the + // Called on the SyncBackendHost sync_thread_ to nudge/pause/resume the // syncer. void DoRequestNudge(const tracked_objects::Location& location); void DoRequestClearServerData(); @@ -361,7 +361,7 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar { // Sets |deferred_nudge_for_cleanup_requested_| to true. See comment below. void DeferNudgeForCleanup(); - // Called on our SyncBackendHost's |core_thread_| to set the passphrase + // Called on our SyncBackendHost's |sync_thread_| to set the passphrase // on behalf of SyncBackendHost::SupplyPassphrase. void DoSetPassphrase(const std::string& passphrase, bool is_explicit); @@ -371,22 +371,23 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar { bool processing_passphrase() const; void set_processing_passphrase(); - // Called on SyncBackendHost's |core_thread_| to set the datatypes we need + // Called on SyncBackendHost's |sync_thread_| to set the datatypes we need // to encrypt as well as encrypt all local data of that type. void DoEncryptDataTypes(const syncable::ModelTypeSet& encrypted_types); // The shutdown order is a bit complicated: - // 1) From |core_thread_|, invoke the syncapi Shutdown call to do a final - // SaveChanges, close sqlite handles, and halt the syncer thread (which - // could potentially block for 1 minute). - // 2) Then, from |frontend_loop_|, halt the core_thread_. This causes - // syncapi thread-exit handlers to run and make use of cached pointers to - // various components owned implicitly by us. - // 3) Destroy this Core. That will delete syncapi components in a safe order - // because the thread that was using them has exited (in step 2). + // 1) From |sync_thread_|, invoke the syncapi Shutdown call to do + // a final SaveChanges, and close sqlite handles. + // 2) Then, from |frontend_loop_|, halt the sync_thread_ (which is + // a blocking call). This causes syncapi thread-exit handlers + // to run and make use of cached pointers to various components + // owned implicitly by us. + // 3) Destroy this Core. That will delete syncapi components in a + // safe order because the thread that was using them has exited + // (in step 2). void DoShutdown(bool stopping_sync); - // Posts a config request on the core thread. + // Posts a config request on the sync thread. virtual void DoRequestConfig(const syncable::ModelTypeBitSet& added_types, sync_api::ConfigureReason reason); @@ -444,19 +445,19 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar { // Invoked when initialization of syncapi is complete and we can start // our timer. // This must be called from the thread on which SaveChanges is intended to - // be run on; the host's |core_thread_|. + // be run on; the host's |sync_thread_|. void StartSavingChanges(); // Invoked periodically to tell the syncapi to persist its state // by writing to disk. // This is called from the thread we were created on (which is the - // SyncBackendHost |core_thread_|), using a repeating timer that is kicked + // SyncBackendHost |sync_thread_|), using a repeating timer that is kicked // off as soon as the SyncManager tells us it completed // initialization. void SaveChanges(); - // Dispatched to from HandleAuthErrorEventOnCoreLoop to handle updating - // frontend UI components. + // Dispatched to from OnAuthError to handle updating frontend UI + // components. void HandleAuthErrorEventOnFrontendLoop( const GoogleServiceAuthError& new_auth_error); @@ -535,7 +536,7 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar { // set up initial conditions. virtual void HandleInitializationCompletedOnFrontendLoop(); - // Posts a nudge request on the core thread. + // Posts a nudge request on the sync thread. virtual void RequestNudge(const tracked_objects::Location& location); // Called to finish the job of ConfigureDataTypes once the syncer is in @@ -550,7 +551,7 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar { virtual sync_api::HttpPostProviderFactory* MakeHttpBridgeFactory( net::URLRequestContextGetter* getter); - MessageLoop* core_loop() { return core_thread_.message_loop(); } + MessageLoop* sync_loop() { return sync_thread_.message_loop(); } void set_syncapi_initialized() { syncapi_initialized_ = true; } @@ -599,10 +600,8 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar { sync_api::ConfigureReason reason, bool nigori_enabled); - // A thread we dedicate for use by our Core to perform initialization, - // authentication, handle messages from the syncapi, and periodically tell - // the syncapi to persist itself. - base::Thread core_thread_; + // A thread where all the sync operations happen. + base::Thread sync_thread_; // A reference to the MessageLoop used to construct |this|, so we know how // to safely talk back to the SyncFrontend. @@ -629,7 +628,7 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar { // The syncapi needs to periodically get a consistent snapshot of the state, // and it does so from a different thread. Therefore, we protect creation, // destruction, and re-routing events by acquiring this lock. Note that the - // SyncBackendHost may read (on the UI thread or core thread) from registrar_ + // SyncBackendHost may read (on the UI thread or sync thread) from registrar_ // without acquiring the lock (which is typically "read ModelSafeWorker // pointer value", and then invoke methods), because lifetimes are managed on // the UI thread. Of course, this comment only applies to ModelSafeWorker diff --git a/chrome/browser/sync/test_profile_sync_service.cc b/chrome/browser/sync/test_profile_sync_service.cc index 9c6010a..7453645 100644 --- a/chrome/browser/sync/test_profile_sync_service.cc +++ b/chrome/browser/sync/test_profile_sync_service.cc @@ -79,7 +79,7 @@ sync_api::HttpPostProviderFactory* void SyncBackendHostForProfileSyncTest::InitCore( const Core::DoInitializeOptions& options) { std::wstring user = L"testuser@gmail.com"; - core_loop()->PostTask( + sync_loop()->PostTask( FROM_HERE, NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoInitializeForTest, diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index 4baab80..8ea46516 100644 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -640,8 +640,8 @@ 'browser/sync/engine/syncer_end_command.h', 'browser/sync/engine/syncer_proto_util.cc', 'browser/sync/engine/syncer_proto_util.h', - 'browser/sync/engine/syncer_thread.cc', - 'browser/sync/engine/syncer_thread.h', + 'browser/sync/engine/sync_scheduler.cc', + 'browser/sync/engine/sync_scheduler.h', 'browser/sync/engine/syncer_types.cc', 'browser/sync/engine/syncer_types.h', 'browser/sync/engine/syncer_util.cc', diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index 79aab3b..752091c 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -3151,8 +3151,8 @@ 'browser/sync/engine/process_commit_response_command_unittest.cc', 'browser/sync/engine/syncapi_unittest.cc', 'browser/sync/engine/syncer_proto_util_unittest.cc', - 'browser/sync/engine/syncer_thread_unittest.cc', - 'browser/sync/engine/syncer_thread_whitebox_unittest.cc', + 'browser/sync/engine/sync_scheduler_unittest.cc', + 'browser/sync/engine/sync_scheduler_whitebox_unittest.cc', 'browser/sync/engine/syncer_unittest.cc', 'browser/sync/engine/syncproto_unittest.cc', 'browser/sync/engine/syncapi_mock.h', |