| Index: sync/engine/sync_scheduler_impl.cc
|
| diff --git a/sync/engine/sync_scheduler_impl.cc b/sync/engine/sync_scheduler_impl.cc
|
| index 0f55c5fa8dd99cb944adfcef61eea1ccbb272d78..5bfcbcc18dddb72bc2e46ec9acc878d63cc549d2 100644
|
| --- a/sync/engine/sync_scheduler_impl.cc
|
| +++ b/sync/engine/sync_scheduler_impl.cc
|
| @@ -343,15 +343,13 @@ SyncSchedulerImpl::DecideWhileInWaitInterval(const SyncSessionJob& job,
|
| JobPriority priority) {
|
| DCHECK_EQ(MessageLoop::current(), sync_loop_);
|
| DCHECK(wait_interval_.get());
|
| + DCHECK_NE(job.purpose(), SyncSessionJob::POLL);
|
|
|
| SDVLOG(2) << "DecideWhileInWaitInterval with WaitInterval mode "
|
| << WaitInterval::GetModeString(wait_interval_->mode)
|
| << (wait_interval_->had_nudge ? " (had nudge)" : "")
|
| << ((priority == CANARY_PRIORITY) ? " (canary)" : "");
|
|
|
| - if (job.purpose() == SyncSessionJob::POLL)
|
| - return DROP;
|
| -
|
| // If we save a job while in a WaitInterval, there is a well-defined moment
|
| // in time in the future when it makes sense for that SAVE-worthy job to try
|
| // running again -- the end of the WaitInterval.
|
| @@ -383,6 +381,10 @@ SyncSchedulerImpl::JobProcessDecision SyncSchedulerImpl::DecideOnJob(
|
| JobPriority priority) {
|
| DCHECK_EQ(MessageLoop::current(), sync_loop_);
|
|
|
| + // POLL jobs do not call this function.
|
| + DCHECK(job.purpose() == SyncSessionJob::NUDGE ||
|
| + job.purpose() == SyncSessionJob::CONFIGURATION);
|
| +
|
| // See if our type is throttled.
|
| ModelTypeSet throttled_types =
|
| session_context_->throttled_data_type_tracker()->GetThrottledTypes();
|
| @@ -412,10 +414,8 @@ SyncSchedulerImpl::JobProcessDecision SyncSchedulerImpl::DecideOnJob(
|
| if (mode_ == CONFIGURATION_MODE) {
|
| if (job.purpose() == SyncSessionJob::NUDGE)
|
| return SAVE; // Running requires a mode switch.
|
| - else if (job.purpose() == SyncSessionJob::CONFIGURATION)
|
| + else // Implies job.purpose() == SyncSessionJob::CONFIGURATION.
|
| return CONTINUE;
|
| - else
|
| - return DROP;
|
| }
|
|
|
| // We are in normal mode.
|
| @@ -427,26 +427,16 @@ SyncSchedulerImpl::JobProcessDecision SyncSchedulerImpl::DecideOnJob(
|
| // 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
|
| + // 1) |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
|
| + // 2) |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 --
|
| + // We avoid cases 1 and 2 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
|
| @@ -564,6 +554,9 @@ void SyncSchedulerImpl::ScheduleNudgeWithStatesAsync(
|
| nudge_location);
|
| }
|
|
|
| +
|
| +// TODO(zea): Consider adding separate throttling/backoff for datatype
|
| +// refresh requests.
|
| void SyncSchedulerImpl::ScheduleNudgeImpl(
|
| const TimeDelta& delay,
|
| GetUpdatesCallerInfo::GetUpdatesSource source,
|
| @@ -572,6 +565,11 @@ void SyncSchedulerImpl::ScheduleNudgeImpl(
|
| DCHECK_EQ(MessageLoop::current(), sync_loop_);
|
| DCHECK(!invalidation_map.empty()) << "Nudge scheduled for no types!";
|
|
|
| + if (no_scheduling_allowed_) {
|
| + NOTREACHED() << "Illegal to schedule job while session in progress.";
|
| + return;
|
| + }
|
| +
|
| if (!started_) {
|
| SDVLOG_LOC(nudge_location, 2)
|
| << "Dropping nudge, scheduler is not running.";
|
| @@ -631,9 +629,20 @@ void SyncSchedulerImpl::ScheduleNudgeImpl(
|
| DCHECK(!wait_interval_ || !wait_interval_->had_nudge);
|
| }
|
|
|
| - // TODO(zea): Consider adding separate throttling/backoff for datatype
|
| - // refresh requests.
|
| - ScheduleSyncSessionJob(nudge_location, job.Pass());
|
| + TimeDelta run_delay = job->scheduled_start() - TimeTicks::Now();
|
| + if (run_delay < TimeDelta::FromMilliseconds(0))
|
| + run_delay = TimeDelta::FromMilliseconds(0);
|
| + SDVLOG_LOC(nudge_location, 2)
|
| + << "Scheduling a nudge with "
|
| + << run_delay.InMilliseconds() << " ms delay";
|
| +
|
| + pending_nudge_ = job.get();
|
| + PostDelayedTask(nudge_location, "DoSyncSessionJob",
|
| + base::Bind(base::IgnoreResult(&SyncSchedulerImpl::DoSyncSessionJob),
|
| + weak_ptr_factory_.GetWeakPtr(),
|
| + base::Passed(&job),
|
| + NORMAL_PRIORITY),
|
| + run_delay);
|
| }
|
|
|
| const char* SyncSchedulerImpl::GetModeString(SyncScheduler::Mode mode) {
|
| @@ -679,40 +688,6 @@ void SyncSchedulerImpl::PostDelayedTask(
|
| sync_loop_->PostDelayedTask(from_here, task, delay);
|
| }
|
|
|
| -void SyncSchedulerImpl::ScheduleSyncSessionJob(
|
| - const tracked_objects::Location& loc,
|
| - scoped_ptr<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);
|
| - SDVLOG_LOC(loc, 2)
|
| - << "In ScheduleSyncSessionJob with "
|
| - << SyncSessionJob::GetPurposeString(job->purpose())
|
| - << " job and " << delay.InMilliseconds() << " ms delay";
|
| -
|
| - DCHECK(job->purpose() == SyncSessionJob::NUDGE ||
|
| - job->purpose() == SyncSessionJob::POLL);
|
| - if (job->purpose() == SyncSessionJob::NUDGE) {
|
| - SDVLOG_LOC(loc, 2) << "Resetting pending_nudge to ";
|
| - DCHECK(!pending_nudge_ || pending_nudge_->session() ==
|
| - job->session());
|
| - pending_nudge_ = job.get();
|
| - }
|
| -
|
| - PostDelayedTask(loc, "DoSyncSessionJob",
|
| - base::Bind(base::IgnoreResult(&SyncSchedulerImpl::DoSyncSessionJob),
|
| - weak_ptr_factory_.GetWeakPtr(),
|
| - base::Passed(&job),
|
| - NORMAL_PRIORITY),
|
| - delay);
|
| -}
|
| -
|
| bool SyncSchedulerImpl::DoSyncSessionJob(scoped_ptr<SyncSessionJob> job,
|
| JobPriority priority) {
|
| DCHECK_EQ(MessageLoop::current(), sync_loop_);
|
| @@ -756,7 +731,78 @@ bool SyncSchedulerImpl::DoSyncSessionJob(scoped_ptr<SyncSessionJob> job,
|
| job->end_step());
|
| SDVLOG(2) << "Done SyncShare, returned: " << premature_exit;
|
|
|
| - return FinishSyncSessionJob(job.Pass(), premature_exit);
|
| + bool success = FinishSyncSessionJob(job.get(), premature_exit);
|
| +
|
| + if (IsSyncingCurrentlySilenced()) {
|
| + SDVLOG(2) << "We are currently throttled; scheduling Unthrottle.";
|
| + // If we're here, it's because |job| was silenced until a server specified
|
| + // time. (Note, it had to be |job|, because DecideOnJob would not permit
|
| + // any job through while in WaitInterval::THROTTLED).
|
| + scoped_ptr<SyncSessionJob> clone = job->Clone();
|
| + if (clone->purpose() == SyncSessionJob::NUDGE)
|
| + pending_nudge_ = clone.get();
|
| + else if (clone->purpose() == SyncSessionJob::CONFIGURATION)
|
| + wait_interval_->pending_configure_job = clone.get();
|
| + else
|
| + NOTREACHED();
|
| +
|
| + RestartWaiting(clone.Pass());
|
| + return success;
|
| + }
|
| +
|
| + if (!success)
|
| + ScheduleNextSync(job.Pass());
|
| +
|
| + return success;
|
| +}
|
| +
|
| +bool SyncSchedulerImpl::ShouldPoll() {
|
| + if (wait_interval_.get()) {
|
| + SDVLOG(2) << "Not running poll in wait interval.";
|
| + return false;
|
| + }
|
| +
|
| + if (mode_ == CONFIGURATION_MODE) {
|
| + SDVLOG(2) << "Not running poll in configuration mode.";
|
| + return false;
|
| + }
|
| +
|
| + // TODO(rlarocque): Refactor decision-making logic common to all types
|
| + // of jobs into a shared function.
|
| +
|
| + if (session_context_->connection_manager()->HasInvalidAuthToken()) {
|
| + SDVLOG(2) << "Not running poll because auth token is invalid.";
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +void SyncSchedulerImpl::DoPollSyncSessionJob(scoped_ptr<SyncSessionJob> job) {
|
| + DCHECK_EQ(job->purpose(), SyncSessionJob::POLL);
|
| +
|
| + base::AutoReset<bool> protector(&no_scheduling_allowed_, true);
|
| +
|
| + if (!ShouldPoll())
|
| + return;
|
| +
|
| + SDVLOG(2) << "Calling SyncShare with "
|
| + << SyncSessionJob::GetPurposeString(job->purpose()) << " job";
|
| + bool premature_exit = !syncer_->SyncShare(job->mutable_session(),
|
| + job->start_step(),
|
| + job->end_step());
|
| + SDVLOG(2) << "Done SyncShare, returned: " << premature_exit;
|
| +
|
| + FinishSyncSessionJob(job.get(), premature_exit);
|
| +
|
| + if (IsSyncingCurrentlySilenced()) {
|
| + // This will start the countdown to unthrottle. Other kinds of jobs would
|
| + // schedule themselves as the post-unthrottle canary. A poll job is not
|
| + // that urgent, so it does not get to be the canary. We still need to start
|
| + // the timer regardless. Otherwise there could be no one to clear the
|
| + // WaitInterval when the throttling expires.
|
| + RestartWaiting(scoped_ptr<SyncSessionJob>());
|
| + }
|
| }
|
|
|
| void SyncSchedulerImpl::UpdateNudgeTimeRecords(const SyncSourceInfo& info) {
|
| @@ -784,7 +830,7 @@ void SyncSchedulerImpl::UpdateNudgeTimeRecords(const SyncSourceInfo& info) {
|
| }
|
| }
|
|
|
| -bool SyncSchedulerImpl::FinishSyncSessionJob(scoped_ptr<SyncSessionJob> job,
|
| +bool SyncSchedulerImpl::FinishSyncSessionJob(SyncSessionJob* job,
|
| bool exited_prematurely) {
|
| DCHECK_EQ(MessageLoop::current(), sync_loop_);
|
|
|
| @@ -796,15 +842,8 @@ bool SyncSchedulerImpl::FinishSyncSessionJob(scoped_ptr<SyncSessionJob> job,
|
| }
|
|
|
| SDVLOG(2) << "Updating the next polling time after SyncMain";
|
| - ScheduleNextSync(job.Pass(), succeeded);
|
| - return succeeded;
|
| -}
|
|
|
| -void SyncSchedulerImpl::ScheduleNextSync(
|
| - scoped_ptr<SyncSessionJob> finished_job, bool succeeded) {
|
| - DCHECK_EQ(MessageLoop::current(), sync_loop_);
|
| -
|
| - AdjustPolling(finished_job.get());
|
| + AdjustPolling(job);
|
|
|
| if (succeeded) {
|
| // No job currently supported by the scheduler could succeed without
|
| @@ -813,29 +852,16 @@ void SyncSchedulerImpl::ScheduleNextSync(
|
| wait_interval_.reset();
|
| NotifyRetryTime(base::Time());
|
| SDVLOG(2) << "Job succeeded so not scheduling more jobs";
|
| - return;
|
| }
|
|
|
| - if (IsSyncingCurrentlySilenced()) {
|
| - SDVLOG(2) << "We are currently throttled; scheduling Unthrottle.";
|
| - // If we're here, it's because |job| was silenced until a server specified
|
| - // time. (Note, it had to be |job|, because DecideOnJob would not permit
|
| - // any job through while in WaitInterval::THROTTLED).
|
| - scoped_ptr<SyncSessionJob> clone = finished_job->Clone();
|
| - if (clone->purpose() == SyncSessionJob::NUDGE)
|
| - pending_nudge_ = clone.get();
|
| - else if (clone->purpose() == SyncSessionJob::CONFIGURATION)
|
| - wait_interval_->pending_configure_job = clone.get();
|
| - else
|
| - clone.reset(); // Unthrottling is enough, no need to force a canary.
|
| -
|
| - RestartWaiting(clone.Pass());
|
| - return;
|
| - }
|
| + return succeeded;
|
| +}
|
|
|
| - if (finished_job->purpose() == SyncSessionJob::POLL) {
|
| - return; // We don't retry POLL jobs.
|
| - }
|
| +void SyncSchedulerImpl::ScheduleNextSync(
|
| + scoped_ptr<SyncSessionJob> finished_job) {
|
| + DCHECK_EQ(MessageLoop::current(), sync_loop_);
|
| + DCHECK(finished_job->purpose() == SyncSessionJob::CONFIGURATION
|
| + || finished_job->purpose() == SyncSessionJob::NUDGE);
|
|
|
| // TODO(rlarocque): There's no reason why we should blindly backoff and retry
|
| // if we don't succeed. Some types of errors are not likely to disappear on
|
| @@ -980,7 +1006,7 @@ void SyncSchedulerImpl::DoCanaryJob(scoped_ptr<SyncSessionJob> to_be_canary) {
|
| pending_nudge_->session() != to_be_canary->session()) {
|
| // |job| is abandoned.
|
| SDVLOG(2) << "Dropping a nudge in "
|
| - << "DoSyncSessionJob because another nudge was scheduled";
|
| + << "DoCanaryJob because another nudge was scheduled";
|
| return;
|
| }
|
| DCHECK_EQ(pending_nudge_->session(), to_be_canary->session());
|
| @@ -1035,7 +1061,18 @@ void SyncSchedulerImpl::PollTimerCallback() {
|
| TimeTicks::Now(),
|
| s.Pass(),
|
| ConfigurationParams()));
|
| - ScheduleSyncSessionJob(FROM_HERE, job.Pass());
|
| + if (no_scheduling_allowed_) {
|
| + // The no_scheduling_allowed_ flag is set by a function-scoped AutoReset in
|
| + // functions that are called only on the sync thread. This function is also
|
| + // called only on the sync thread, and only when it is posted by an expiring
|
| + // timer. If we find that no_scheduling_allowed_ is set here, then
|
| + // something is very wrong. Maybe someone mistakenly called us directly, or
|
| + // mishandled the book-keeping for no_scheduling_allowed_.
|
| + NOTREACHED() << "Illegal to schedule job while session in progress.";
|
| + return;
|
| + }
|
| +
|
| + DoPollSyncSessionJob(job.Pass());
|
| }
|
|
|
| void SyncSchedulerImpl::Unthrottle(scoped_ptr<SyncSessionJob> to_be_canary) {
|
|
|