diff options
Diffstat (limited to 'sync/engine/sync_scheduler_impl.cc')
-rw-r--r-- | sync/engine/sync_scheduler_impl.cc | 101 |
1 files changed, 79 insertions, 22 deletions
diff --git a/sync/engine/sync_scheduler_impl.cc b/sync/engine/sync_scheduler_impl.cc index 195eda4..d5f9dff 100644 --- a/sync/engine/sync_scheduler_impl.cc +++ b/sync/engine/sync_scheduler_impl.cc @@ -7,6 +7,7 @@ #include <algorithm> #include <cstring> +#include "base/auto_reset.h" #include "base/bind.h" #include "base/compiler_specific.h" #include "base/location.h" @@ -206,7 +207,8 @@ SyncSchedulerImpl::SyncSchedulerImpl(const std::string& name, connection_code_(HttpResponse::SERVER_CONNECTION_OK), delay_provider_(delay_provider), syncer_(syncer), - session_context_(context) { + session_context_(context), + no_scheduling_allowed_(false) { DCHECK(sync_loop_); } @@ -275,6 +277,13 @@ void SyncSchedulerImpl::Start(Mode mode) { if (old_mode != mode_) { // We just changed our mode. See if there are any pending jobs that we could // execute in the new mode. + if (mode_ == NORMAL_MODE) { + // It is illegal to switch to NORMAL_MODE if a previous CONFIGURATION job + // has not yet completed. + DCHECK(!wait_interval_.get() || + !wait_interval_->pending_configure_job.get()); + } + DoPendingJobIfPossible(false); } } @@ -450,12 +459,43 @@ SyncSchedulerImpl::JobProcessDecision SyncSchedulerImpl::DecideOnJob( DCHECK_EQ(mode_, NORMAL_MODE); DCHECK_NE(job.purpose, SyncSessionJob::CONFIGURATION); - // Freshness condition - if (job.scheduled_start < last_sync_session_end_time_) { - SDVLOG(2) << "Dropping job because of freshness"; - return DROP; - } + // Note about some subtle scheduling semantics. + // + // It's possible at this point that |job| is known to be unnecessary, and + // dropping it would be perfectly safe and correct. Consider + // + // 1) |job| is a POLL with a |scheduled_start| time that is less than + // the time that the last successful all-datatype NUDGE completed. + // + // 2) |job| is a NUDGE (for any combination of types) with a + // |scheduled_start| time that is less than the time that the last + // successful all-datatype NUDGE completed, and it has a NOTIFICATION + // GetUpdatesCallerInfo value yet offers no new notification hint. + // + // 3) |job| is a NUDGE with a |scheduled_start| time that is less than + // the time that the last successful matching-datatype NUDGE completed, + // and payloads (hints) are identical to that last successful NUDGE. + // + // Case 1 can occur if the POLL timer fires *after* a call to + // ScheduleSyncSessionJob for a NUDGE, but *before* the thread actually + // picks the resulting posted task off of the MessageLoop. The NUDGE will + // run first and complete at a time greater than the POLL scheduled_start. + // However, this case (and POLLs in general) is so rare that we ignore it ( + // and avoid the required bookeeping to simplify code). + // + // We avoid cases 2 and 3 by externally synchronizing NUDGE requests -- + // scheduling a NUDGE requires command of the sync thread, which is + // impossible* from outside of SyncScheduler if a NUDGE is taking place. + // And if you have command of the sync thread when scheduling a NUDGE and a + // previous NUDGE exists, they will be coalesced and the stale job will be + // cancelled via the session-equality check in DoSyncSessionJob. + // + // * It's not strictly "impossible", but it would be reentrant and hence + // illegal. e.g. scheduling a job and re-entering the SyncScheduler is NOT a + // legal side effect of any of the work being done as part of a sync cycle. + // See |no_scheduling_allowed_| for details. + // Decision now rests on state of auth tokens. if (!session_context_->connection_manager()->HasInvalidAuthToken()) return CONTINUE; @@ -469,6 +509,9 @@ void SyncSchedulerImpl::InitOrCoalescePendingJob(const SyncSessionJob& job) { if (pending_nudge_.get() == NULL) { SDVLOG(2) << "Creating a pending nudge job"; SyncSession* s = job.session.get(); + + // Get a fresh session with similar configuration as before (resets + // StatusController). scoped_ptr<SyncSession> session(new SyncSession(s->context(), s->delegate(), s->source(), s->routing_info(), s->workers())); @@ -476,7 +519,6 @@ void SyncSchedulerImpl::InitOrCoalescePendingJob(const SyncSessionJob& job) { make_linked_ptr(session.release()), false, ConfigurationParams(), job.from_here); pending_nudge_.reset(new SyncSessionJob(new_job)); - return; } @@ -587,6 +629,7 @@ void SyncSchedulerImpl::ScheduleNudgeImpl( const ModelTypeStateMap& type_state_map, bool is_canary_job, const tracked_objects::Location& nudge_location) { DCHECK_EQ(MessageLoop::current(), sync_loop_); + DCHECK(!type_state_map.empty()) << "Nudge scheduled for no types!"; SDVLOG_LOC(nudge_location, 2) << "In ScheduleNudgeImpl with delay " @@ -597,6 +640,7 @@ void SyncSchedulerImpl::ScheduleNudgeImpl( << (is_canary_job ? " (canary)" : ""); SyncSourceInfo info(source, type_state_map); + UpdateNudgeTimeRecords(info); SyncSession* session(CreateSyncSession(info)); SyncSessionJob job(SyncSessionJob::NUDGE, TimeTicks::Now() + delay, @@ -700,6 +744,11 @@ void SyncSchedulerImpl::PostDelayedTask( void SyncSchedulerImpl::ScheduleSyncSessionJob(const SyncSessionJob& job) { DCHECK_EQ(MessageLoop::current(), sync_loop_); + if (no_scheduling_allowed_) { + NOTREACHED() << "Illegal to schedule job while session in progress."; + return; + } + TimeDelta delay = job.scheduled_start - TimeTicks::Now(); if (delay < TimeDelta::FromMilliseconds(0)) delay = TimeDelta::FromMilliseconds(0); @@ -725,6 +774,8 @@ void SyncSchedulerImpl::ScheduleSyncSessionJob(const SyncSessionJob& job) { void SyncSchedulerImpl::DoSyncSessionJob(const SyncSessionJob& job) { DCHECK_EQ(MessageLoop::current(), sync_loop_); + + AutoReset<bool> protector(&no_scheduling_allowed_, true); if (!ShouldRunJob(job)) { SLOG(WARNING) << "Not executing " @@ -769,24 +820,33 @@ void SyncSchedulerImpl::DoSyncSessionJob(const SyncSessionJob& job) { FinishSyncSessionJob(job); } -void SyncSchedulerImpl::FinishSyncSessionJob(const SyncSessionJob& job) { +void SyncSchedulerImpl::UpdateNudgeTimeRecords(const SyncSourceInfo& info) { DCHECK_EQ(MessageLoop::current(), sync_loop_); - // Update timing information for how often datatypes are triggering nudges. + + // We are interested in recording time between local nudges for datatypes. + // TODO(tim): Consider tracking LOCAL_NOTIFICATION as well. + if (info.updates_source != GetUpdatesCallerInfo::LOCAL) + return; + base::TimeTicks now = TimeTicks::Now(); - if (!last_sync_session_end_time_.is_null()) { - ModelTypeStateMap::const_iterator iter; - for (iter = job.session->source().types.begin(); - iter != job.session->source().types.end(); - ++iter) { + // Update timing information for how often datatypes are triggering nudges. + for (ModelTypeStateMap::const_iterator iter = info.types.begin(); + iter != info.types.end(); + ++iter) { + base::TimeTicks previous = last_local_nudges_by_model_type_[iter->first]; + last_local_nudges_by_model_type_[iter->first] = now; + if (previous.is_null()) + continue; + #define PER_DATA_TYPE_MACRO(type_str) \ - SYNC_FREQ_HISTOGRAM("Sync.Freq" type_str, \ - now - last_sync_session_end_time_); + SYNC_FREQ_HISTOGRAM("Sync.Freq" type_str, now - previous); SYNC_DATA_TYPE_HISTOGRAM(iter->first); #undef PER_DATA_TYPE_MACRO - } } - last_sync_session_end_time_ = now; +} +void SyncSchedulerImpl::FinishSyncSessionJob(const SyncSessionJob& job) { + DCHECK_EQ(MessageLoop::current(), sync_loop_); // Now update the status of the connection from SCM. We need this to decide // whether we need to save/run future jobs. The notifications from SCM are not // reliable. @@ -808,6 +868,7 @@ void SyncSchedulerImpl::FinishSyncSessionJob(const SyncSessionJob& job) { !job.config_params.ready_task.is_null()) { // If this was a configuration job with a ready task, invoke it now that // we finished successfully. + AutoReset<bool> protector(&no_scheduling_allowed_, true); job.config_params.ready_task.Run(); } @@ -974,10 +1035,6 @@ void SyncSchedulerImpl::DoPendingJobIfPossible(bool is_canary_job) { job_to_execute = wait_interval_->pending_configure_job.get(); } else if (mode_ == NORMAL_MODE && pending_nudge_.get()) { SDVLOG(2) << "Found pending nudge job"; - // Pending jobs mostly have time from the past. Reset it so this job - // will get executed. - if (pending_nudge_->scheduled_start < TimeTicks::Now()) - pending_nudge_->scheduled_start = TimeTicks::Now(); scoped_ptr<SyncSession> session(CreateSyncSession( pending_nudge_->session->source())); |