OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "sync/engine/sync_scheduler_impl.h" | 5 #include "sync/engine/sync_scheduler_impl.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <cstring> | 8 #include <cstring> |
9 | 9 |
| 10 #include "base/auto_reset.h" |
10 #include "base/bind.h" | 11 #include "base/bind.h" |
11 #include "base/bind_helpers.h" | 12 #include "base/bind_helpers.h" |
12 #include "base/compiler_specific.h" | 13 #include "base/compiler_specific.h" |
13 #include "base/location.h" | 14 #include "base/location.h" |
14 #include "base/logging.h" | 15 #include "base/logging.h" |
15 #include "base/message_loop/message_loop.h" | 16 #include "base/message_loop/message_loop.h" |
16 #include "sync/engine/backoff_delay_provider.h" | 17 #include "sync/engine/backoff_delay_provider.h" |
17 #include "sync/engine/syncer.h" | 18 #include "sync/engine/syncer.h" |
18 #include "sync/protocol/proto_enum_conversions.h" | 19 #include "sync/protocol/proto_enum_conversions.h" |
19 #include "sync/protocol/sync.pb.h" | 20 #include "sync/protocol/sync.pb.h" |
(...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
148 SyncSchedulerImpl::SyncSchedulerImpl(const std::string& name, | 149 SyncSchedulerImpl::SyncSchedulerImpl(const std::string& name, |
149 BackoffDelayProvider* delay_provider, | 150 BackoffDelayProvider* delay_provider, |
150 sessions::SyncSessionContext* context, | 151 sessions::SyncSessionContext* context, |
151 Syncer* syncer) | 152 Syncer* syncer) |
152 : name_(name), | 153 : name_(name), |
153 started_(false), | 154 started_(false), |
154 syncer_short_poll_interval_seconds_( | 155 syncer_short_poll_interval_seconds_( |
155 TimeDelta::FromSeconds(kDefaultShortPollIntervalSeconds)), | 156 TimeDelta::FromSeconds(kDefaultShortPollIntervalSeconds)), |
156 syncer_long_poll_interval_seconds_( | 157 syncer_long_poll_interval_seconds_( |
157 TimeDelta::FromSeconds(kDefaultLongPollIntervalSeconds)), | 158 TimeDelta::FromSeconds(kDefaultLongPollIntervalSeconds)), |
158 mode_(CONFIGURATION_MODE), | 159 mode_(NORMAL_MODE), |
159 delay_provider_(delay_provider), | 160 delay_provider_(delay_provider), |
160 syncer_(syncer), | 161 syncer_(syncer), |
161 session_context_(context), | 162 session_context_(context), |
162 last_poll_reset_(base::TimeTicks::Now()), | 163 no_scheduling_allowed_(false), |
| 164 do_poll_after_credentials_updated_(false), |
163 next_sync_session_job_priority_(NORMAL_PRIORITY), | 165 next_sync_session_job_priority_(NORMAL_PRIORITY), |
164 weak_ptr_factory_(this), | 166 weak_ptr_factory_(this), |
165 weak_ptr_factory_for_weak_handle_(this) { | 167 weak_ptr_factory_for_weak_handle_(this) { |
166 weak_handle_this_ = MakeWeakHandle( | 168 weak_handle_this_ = MakeWeakHandle( |
167 weak_ptr_factory_for_weak_handle_.GetWeakPtr()); | 169 weak_ptr_factory_for_weak_handle_.GetWeakPtr()); |
168 } | 170 } |
169 | 171 |
170 SyncSchedulerImpl::~SyncSchedulerImpl() { | 172 SyncSchedulerImpl::~SyncSchedulerImpl() { |
171 DCHECK(CalledOnValidThread()); | 173 DCHECK(CalledOnValidThread()); |
172 Stop(); | 174 Stop(); |
(...skipping 25 matching lines...) Expand all Loading... |
198 // 3. A nudge was saved previously due to not having a valid auth token. | 200 // 3. A nudge was saved previously due to not having a valid auth token. |
199 // 4. A nudge was scheduled + saved while in configuration mode. | 201 // 4. A nudge was scheduled + saved while in configuration mode. |
200 // | 202 // |
201 // In all cases except (2), we want to retry contacting the server. We | 203 // In all cases except (2), we want to retry contacting the server. We |
202 // call TryCanaryJob to achieve this, and note that nothing -- not even a | 204 // call TryCanaryJob to achieve this, and note that nothing -- not even a |
203 // canary job -- can bypass a THROTTLED WaitInterval. The only thing that | 205 // canary job -- can bypass a THROTTLED WaitInterval. The only thing that |
204 // has the authority to do that is the Unthrottle timer. | 206 // has the authority to do that is the Unthrottle timer. |
205 TryCanaryJob(); | 207 TryCanaryJob(); |
206 } | 208 } |
207 | 209 |
208 void SyncSchedulerImpl::Start(Mode mode, base::Time last_poll_time) { | 210 void SyncSchedulerImpl::Start(Mode mode) { |
209 DCHECK(CalledOnValidThread()); | 211 DCHECK(CalledOnValidThread()); |
210 std::string thread_name = base::MessageLoop::current()->thread_name(); | 212 std::string thread_name = base::MessageLoop::current()->thread_name(); |
211 if (thread_name.empty()) | 213 if (thread_name.empty()) |
212 thread_name = "<Main thread>"; | 214 thread_name = "<Main thread>"; |
213 SDVLOG(2) << "Start called from thread " | 215 SDVLOG(2) << "Start called from thread " |
214 << thread_name << " with mode " << GetModeString(mode); | 216 << thread_name << " with mode " << GetModeString(mode); |
215 if (!started_) { | 217 if (!started_) { |
216 started_ = true; | 218 started_ = true; |
217 SendInitialSnapshot(); | 219 SendInitialSnapshot(); |
218 } | 220 } |
219 | 221 |
220 DCHECK(!session_context_->account_name().empty()); | 222 DCHECK(!session_context_->account_name().empty()); |
221 DCHECK(syncer_.get()); | 223 DCHECK(syncer_.get()); |
222 Mode old_mode = mode_; | 224 Mode old_mode = mode_; |
223 mode_ = mode; | 225 mode_ = mode; |
224 // Only adjust the poll reset time if it was valid and in the past. | 226 AdjustPolling(UPDATE_INTERVAL); // Will kick start poll timer if needed. |
225 if (!last_poll_time.is_null() && last_poll_time < base::Time::Now()) { | |
226 // Convert from base::Time to base::TimeTicks. The reason we use Time | |
227 // for persisting is that TimeTicks can stop making forward progress when | |
228 // the machine is suspended. This implies that on resume the client might | |
229 // actually have miss the real poll, unless the client is restarted. Fixing | |
230 // that would require using an AlarmTimer though, which is only supported | |
231 // on certain platforms. | |
232 last_poll_reset_ = | |
233 base::TimeTicks::Now() - (base::Time::Now() - last_poll_time); | |
234 } | |
235 | 227 |
236 if (old_mode != mode_ && mode_ == NORMAL_MODE) { | 228 if (old_mode != mode_ && mode_ == NORMAL_MODE) { |
237 // We just got back to normal mode. Let's try to run the work that was | 229 // We just got back to normal mode. Let's try to run the work that was |
238 // queued up while we were configuring. | 230 // queued up while we were configuring. |
239 | 231 |
240 AdjustPolling(UPDATE_INTERVAL); // Will kick start poll timer if needed. | |
241 | |
242 // Update our current time before checking IsRetryRequired(). | 232 // Update our current time before checking IsRetryRequired(). |
243 nudge_tracker_.SetSyncCycleStartTime(base::TimeTicks::Now()); | 233 nudge_tracker_.SetSyncCycleStartTime(base::TimeTicks::Now()); |
244 if (nudge_tracker_.IsSyncRequired() && CanRunNudgeJobNow(NORMAL_PRIORITY)) { | 234 if (nudge_tracker_.IsSyncRequired() && CanRunNudgeJobNow(NORMAL_PRIORITY)) { |
245 TrySyncSessionJob(); | 235 TrySyncSessionJob(); |
246 } | 236 } |
247 } | 237 } |
248 } | 238 } |
249 | 239 |
250 ModelTypeSet SyncSchedulerImpl::GetEnabledAndUnthrottledTypes() { | 240 ModelTypeSet SyncSchedulerImpl::GetEnabledAndUnthrottledTypes() { |
251 ModelTypeSet enabled_types = session_context_->GetEnabledTypes(); | 241 ModelTypeSet enabled_types = session_context_->GetEnabledTypes(); |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
309 pending_configure_params_.reset(new ConfigurationParams(params)); | 299 pending_configure_params_.reset(new ConfigurationParams(params)); |
310 TrySyncSessionJob(); | 300 TrySyncSessionJob(); |
311 } else { | 301 } else { |
312 SDVLOG(2) << "No change in routing info, calling ready task directly."; | 302 SDVLOG(2) << "No change in routing info, calling ready task directly."; |
313 params.ready_task.Run(); | 303 params.ready_task.Run(); |
314 } | 304 } |
315 } | 305 } |
316 | 306 |
317 bool SyncSchedulerImpl::CanRunJobNow(JobPriority priority) { | 307 bool SyncSchedulerImpl::CanRunJobNow(JobPriority priority) { |
318 DCHECK(CalledOnValidThread()); | 308 DCHECK(CalledOnValidThread()); |
319 if (IsCurrentlyThrottled()) { | 309 if (wait_interval_ && wait_interval_->mode == WaitInterval::THROTTLED) { |
320 SDVLOG(1) << "Unable to run a job because we're throttled."; | 310 SDVLOG(1) << "Unable to run a job because we're throttled."; |
321 return false; | 311 return false; |
322 } | 312 } |
323 | 313 |
324 if (IsBackingOff() && priority != CANARY_PRIORITY) { | 314 if (wait_interval_ |
| 315 && wait_interval_->mode == WaitInterval::EXPONENTIAL_BACKOFF |
| 316 && priority != CANARY_PRIORITY) { |
325 SDVLOG(1) << "Unable to run a job because we're backing off."; | 317 SDVLOG(1) << "Unable to run a job because we're backing off."; |
326 return false; | 318 return false; |
327 } | 319 } |
328 | 320 |
329 if (session_context_->connection_manager()->HasInvalidAuthToken()) { | 321 if (session_context_->connection_manager()->HasInvalidAuthToken()) { |
330 SDVLOG(1) << "Unable to run a job because we have no valid auth token."; | 322 SDVLOG(1) << "Unable to run a job because we have no valid auth token."; |
331 return false; | 323 return false; |
332 } | 324 } |
333 | 325 |
334 return true; | 326 return true; |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
405 nudge_tracker_.RecordInitialSyncRequired(model_type); | 397 nudge_tracker_.RecordInitialSyncRequired(model_type); |
406 ScheduleNudgeImpl(TimeDelta::FromSeconds(0), FROM_HERE); | 398 ScheduleNudgeImpl(TimeDelta::FromSeconds(0), FROM_HERE); |
407 } | 399 } |
408 | 400 |
409 // TODO(zea): Consider adding separate throttling/backoff for datatype | 401 // TODO(zea): Consider adding separate throttling/backoff for datatype |
410 // refresh requests. | 402 // refresh requests. |
411 void SyncSchedulerImpl::ScheduleNudgeImpl( | 403 void SyncSchedulerImpl::ScheduleNudgeImpl( |
412 const TimeDelta& delay, | 404 const TimeDelta& delay, |
413 const tracked_objects::Location& nudge_location) { | 405 const tracked_objects::Location& nudge_location) { |
414 DCHECK(CalledOnValidThread()); | 406 DCHECK(CalledOnValidThread()); |
415 CHECK(!syncer_->IsSyncing()); | 407 |
| 408 if (no_scheduling_allowed_) { |
| 409 NOTREACHED() << "Illegal to schedule job while session in progress."; |
| 410 return; |
| 411 } |
416 | 412 |
417 if (!started_) { | 413 if (!started_) { |
418 SDVLOG_LOC(nudge_location, 2) | 414 SDVLOG_LOC(nudge_location, 2) |
419 << "Dropping nudge, scheduler is not running."; | 415 << "Dropping nudge, scheduler is not running."; |
420 return; | 416 return; |
421 } | 417 } |
422 | 418 |
423 SDVLOG_LOC(nudge_location, 2) | 419 SDVLOG_LOC(nudge_location, 2) |
424 << "In ScheduleNudgeImpl with delay " | 420 << "In ScheduleNudgeImpl with delay " |
425 << delay.InMilliseconds() << " ms"; | 421 << delay.InMilliseconds() << " ms"; |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
461 nudge_tracker_.SetDefaultNudgeDelay(delay_ms); | 457 nudge_tracker_.SetDefaultNudgeDelay(delay_ms); |
462 } | 458 } |
463 | 459 |
464 void SyncSchedulerImpl::DoNudgeSyncSessionJob(JobPriority priority) { | 460 void SyncSchedulerImpl::DoNudgeSyncSessionJob(JobPriority priority) { |
465 DCHECK(CalledOnValidThread()); | 461 DCHECK(CalledOnValidThread()); |
466 DCHECK(CanRunNudgeJobNow(priority)); | 462 DCHECK(CanRunNudgeJobNow(priority)); |
467 | 463 |
468 DVLOG(2) << "Will run normal mode sync cycle with types " | 464 DVLOG(2) << "Will run normal mode sync cycle with types " |
469 << ModelTypeSetToString(session_context_->GetEnabledTypes()); | 465 << ModelTypeSetToString(session_context_->GetEnabledTypes()); |
470 scoped_ptr<SyncSession> session(SyncSession::Build(session_context_, this)); | 466 scoped_ptr<SyncSession> session(SyncSession::Build(session_context_, this)); |
471 bool success = syncer_->NormalSyncShare( | 467 bool premature_exit = !syncer_->NormalSyncShare( |
472 GetEnabledAndUnthrottledTypes(), &nudge_tracker_, session.get()); | 468 GetEnabledAndUnthrottledTypes(), &nudge_tracker_, session.get()); |
| 469 AdjustPolling(FORCE_RESET); |
| 470 // Don't run poll job till the next time poll timer fires. |
| 471 do_poll_after_credentials_updated_ = false; |
| 472 |
| 473 bool success = !premature_exit |
| 474 && !sessions::HasSyncerError( |
| 475 session->status_controller().model_neutral_state()); |
473 | 476 |
474 if (success) { | 477 if (success) { |
475 // That cycle took care of any outstanding work we had. | 478 // That cycle took care of any outstanding work we had. |
476 SDVLOG(2) << "Nudge succeeded."; | 479 SDVLOG(2) << "Nudge succeeded."; |
477 nudge_tracker_.RecordSuccessfulSyncCycle(); | 480 nudge_tracker_.RecordSuccessfulSyncCycle(); |
478 scheduled_nudge_time_ = base::TimeTicks(); | 481 scheduled_nudge_time_ = base::TimeTicks(); |
479 HandleSuccess(); | |
480 | 482 |
481 // If this was a canary, we may need to restart the poll timer (the poll | 483 // If we're here, then we successfully reached the server. End all backoff. |
482 // timer may have fired while the scheduler was in an error state, ignoring | 484 wait_interval_.reset(); |
483 // the poll). | 485 NotifyRetryTime(base::Time()); |
484 if (!poll_timer_.IsRunning()) { | |
485 SDVLOG(1) << "Canary succeeded, restarting polling."; | |
486 AdjustPolling(UPDATE_INTERVAL); | |
487 } | |
488 } else { | 486 } else { |
489 HandleFailure(session->status_controller().model_neutral_state()); | 487 HandleFailure(session->status_controller().model_neutral_state()); |
490 } | 488 } |
491 } | 489 } |
492 | 490 |
493 void SyncSchedulerImpl::DoConfigurationSyncSessionJob(JobPriority priority) { | 491 void SyncSchedulerImpl::DoConfigurationSyncSessionJob(JobPriority priority) { |
494 DCHECK(CalledOnValidThread()); | 492 DCHECK(CalledOnValidThread()); |
495 DCHECK_EQ(mode_, CONFIGURATION_MODE); | 493 DCHECK_EQ(mode_, CONFIGURATION_MODE); |
496 DCHECK(pending_configure_params_ != NULL); | 494 DCHECK(pending_configure_params_ != NULL); |
497 | 495 |
498 if (!CanRunJobNow(priority)) { | 496 if (!CanRunJobNow(priority)) { |
499 SDVLOG(2) << "Unable to run configure job right now."; | 497 SDVLOG(2) << "Unable to run configure job right now."; |
500 if (!pending_configure_params_->retry_task.is_null()) { | 498 if (!pending_configure_params_->retry_task.is_null()) { |
501 pending_configure_params_->retry_task.Run(); | 499 pending_configure_params_->retry_task.Run(); |
502 pending_configure_params_->retry_task.Reset(); | 500 pending_configure_params_->retry_task.Reset(); |
503 } | 501 } |
504 return; | 502 return; |
505 } | 503 } |
506 | 504 |
507 SDVLOG(2) << "Will run configure SyncShare with types " | 505 SDVLOG(2) << "Will run configure SyncShare with types " |
508 << ModelTypeSetToString(session_context_->GetEnabledTypes()); | 506 << ModelTypeSetToString(session_context_->GetEnabledTypes()); |
509 scoped_ptr<SyncSession> session(SyncSession::Build(session_context_, this)); | 507 scoped_ptr<SyncSession> session(SyncSession::Build(session_context_, this)); |
510 bool success = syncer_->ConfigureSyncShare( | 508 bool premature_exit = !syncer_->ConfigureSyncShare( |
511 pending_configure_params_->types_to_download, | 509 pending_configure_params_->types_to_download, |
512 pending_configure_params_->source, | 510 pending_configure_params_->source, |
513 session.get()); | 511 session.get()); |
| 512 AdjustPolling(FORCE_RESET); |
| 513 // Don't run poll job till the next time poll timer fires. |
| 514 do_poll_after_credentials_updated_ = false; |
| 515 |
| 516 bool success = !premature_exit |
| 517 && !sessions::HasSyncerError( |
| 518 session->status_controller().model_neutral_state()); |
514 | 519 |
515 if (success) { | 520 if (success) { |
516 SDVLOG(2) << "Configure succeeded."; | 521 SDVLOG(2) << "Configure succeeded."; |
517 pending_configure_params_->ready_task.Run(); | 522 pending_configure_params_->ready_task.Run(); |
518 pending_configure_params_.reset(); | 523 pending_configure_params_.reset(); |
519 HandleSuccess(); | 524 |
| 525 // If we're here, then we successfully reached the server. End all backoff. |
| 526 wait_interval_.reset(); |
| 527 NotifyRetryTime(base::Time()); |
520 } else { | 528 } else { |
521 HandleFailure(session->status_controller().model_neutral_state()); | 529 HandleFailure(session->status_controller().model_neutral_state()); |
522 // Sync cycle might receive response from server that causes scheduler to | 530 // Sync cycle might receive response from server that causes scheduler to |
523 // stop and draws pending_configure_params_ invalid. | 531 // stop and draws pending_configure_params_ invalid. |
524 if (started_ && !pending_configure_params_->retry_task.is_null()) { | 532 if (started_ && !pending_configure_params_->retry_task.is_null()) { |
525 pending_configure_params_->retry_task.Run(); | 533 pending_configure_params_->retry_task.Run(); |
526 pending_configure_params_->retry_task.Reset(); | 534 pending_configure_params_->retry_task.Reset(); |
527 } | 535 } |
528 } | 536 } |
529 } | 537 } |
530 | 538 |
531 void SyncSchedulerImpl::HandleSuccess() { | |
532 // If we're here, then we successfully reached the server. End all backoff. | |
533 wait_interval_.reset(); | |
534 NotifyRetryTime(base::Time()); | |
535 } | |
536 | |
537 void SyncSchedulerImpl::HandleFailure( | 539 void SyncSchedulerImpl::HandleFailure( |
538 const sessions::ModelNeutralState& model_neutral_state) { | 540 const sessions::ModelNeutralState& model_neutral_state) { |
539 if (IsCurrentlyThrottled()) { | 541 if (IsCurrentlyThrottled()) { |
540 SDVLOG(2) << "Was throttled during previous sync cycle."; | 542 SDVLOG(2) << "Was throttled during previous sync cycle."; |
| 543 RestartWaiting(); |
541 } else if (!IsBackingOff()) { | 544 } else if (!IsBackingOff()) { |
542 // Setup our backoff if this is our first such failure. | 545 // Setup our backoff if this is our first such failure. |
543 TimeDelta length = delay_provider_->GetDelay( | 546 TimeDelta length = delay_provider_->GetDelay( |
544 delay_provider_->GetInitialDelay(model_neutral_state)); | 547 delay_provider_->GetInitialDelay(model_neutral_state)); |
545 wait_interval_.reset( | 548 wait_interval_.reset( |
546 new WaitInterval(WaitInterval::EXPONENTIAL_BACKOFF, length)); | 549 new WaitInterval(WaitInterval::EXPONENTIAL_BACKOFF, length)); |
547 SDVLOG(2) << "Sync cycle failed. Will back off for " | 550 SDVLOG(2) << "Sync cycle failed. Will back off for " |
548 << wait_interval_->length.InMilliseconds() << "ms."; | 551 << wait_interval_->length.InMilliseconds() << "ms."; |
549 } else { | 552 RestartWaiting(); |
550 // Increase our backoff interval and schedule another retry. | |
551 TimeDelta length = delay_provider_->GetDelay(wait_interval_->length); | |
552 wait_interval_.reset( | |
553 new WaitInterval(WaitInterval::EXPONENTIAL_BACKOFF, length)); | |
554 SDVLOG(2) << "Sync cycle failed. Will back off for " | |
555 << wait_interval_->length.InMilliseconds() << "ms."; | |
556 } | 553 } |
557 RestartWaiting(); | |
558 } | 554 } |
559 | 555 |
560 void SyncSchedulerImpl::DoPollSyncSessionJob() { | 556 void SyncSchedulerImpl::DoPollSyncSessionJob() { |
| 557 base::AutoReset<bool> protector(&no_scheduling_allowed_, true); |
| 558 |
561 SDVLOG(2) << "Polling with types " | 559 SDVLOG(2) << "Polling with types " |
562 << ModelTypeSetToString(GetEnabledAndUnthrottledTypes()); | 560 << ModelTypeSetToString(GetEnabledAndUnthrottledTypes()); |
563 scoped_ptr<SyncSession> session(SyncSession::Build(session_context_, this)); | 561 scoped_ptr<SyncSession> session(SyncSession::Build(session_context_, this)); |
564 bool success = syncer_->PollSyncShare( | 562 syncer_->PollSyncShare( |
565 GetEnabledAndUnthrottledTypes(), | 563 GetEnabledAndUnthrottledTypes(), |
566 session.get()); | 564 session.get()); |
567 | 565 |
568 // Only restart the timer if the poll succeeded. Otherwise rely on normal | 566 AdjustPolling(FORCE_RESET); |
569 // failure handling to retry with backoff. | 567 |
570 if (success) { | 568 if (IsCurrentlyThrottled()) { |
571 AdjustPolling(FORCE_RESET); | 569 SDVLOG(2) << "Poll request got us throttled."; |
572 HandleSuccess(); | 570 // The OnSilencedUntil() call set up the WaitInterval for us. All we need |
573 } else { | 571 // to do is start the timer. |
574 HandleFailure(session->status_controller().model_neutral_state()); | 572 RestartWaiting(); |
575 } | 573 } |
576 } | 574 } |
577 | 575 |
578 void SyncSchedulerImpl::UpdateNudgeTimeRecords(ModelTypeSet types) { | 576 void SyncSchedulerImpl::UpdateNudgeTimeRecords(ModelTypeSet types) { |
579 DCHECK(CalledOnValidThread()); | 577 DCHECK(CalledOnValidThread()); |
580 base::TimeTicks now = TimeTicks::Now(); | 578 base::TimeTicks now = TimeTicks::Now(); |
581 // Update timing information for how often datatypes are triggering nudges. | 579 // Update timing information for how often datatypes are triggering nudges. |
582 for (ModelTypeSet::Iterator iter = types.First(); iter.Good(); iter.Inc()) { | 580 for (ModelTypeSet::Iterator iter = types.First(); iter.Good(); iter.Inc()) { |
583 base::TimeTicks previous = last_local_nudges_by_model_type_[iter.Get()]; | 581 base::TimeTicks previous = last_local_nudges_by_model_type_[iter.Get()]; |
584 last_local_nudges_by_model_type_[iter.Get()] = now; | 582 last_local_nudges_by_model_type_[iter.Get()] = now; |
585 if (previous.is_null()) | 583 if (previous.is_null()) |
586 continue; | 584 continue; |
587 | 585 |
588 #define PER_DATA_TYPE_MACRO(type_str) \ | 586 #define PER_DATA_TYPE_MACRO(type_str) \ |
589 SYNC_FREQ_HISTOGRAM("Sync.Freq" type_str, now - previous); | 587 SYNC_FREQ_HISTOGRAM("Sync.Freq" type_str, now - previous); |
590 SYNC_DATA_TYPE_HISTOGRAM(iter.Get()); | 588 SYNC_DATA_TYPE_HISTOGRAM(iter.Get()); |
591 #undef PER_DATA_TYPE_MACRO | 589 #undef PER_DATA_TYPE_MACRO |
592 } | 590 } |
593 } | 591 } |
594 | 592 |
595 TimeDelta SyncSchedulerImpl::GetPollInterval() { | 593 TimeDelta SyncSchedulerImpl::GetPollInterval() { |
596 return (!session_context_->notifications_enabled() || | 594 return (!session_context_->notifications_enabled() || |
597 !session_context_->ShouldFetchUpdatesBeforeCommit()) ? | 595 !session_context_->ShouldFetchUpdatesBeforeCommit()) ? |
598 syncer_short_poll_interval_seconds_ : | 596 syncer_short_poll_interval_seconds_ : |
599 syncer_long_poll_interval_seconds_; | 597 syncer_long_poll_interval_seconds_; |
600 } | 598 } |
601 | 599 |
602 void SyncSchedulerImpl::AdjustPolling(PollAdjustType type) { | 600 void SyncSchedulerImpl::AdjustPolling(PollAdjustType type) { |
603 DCHECK(CalledOnValidThread()); | 601 DCHECK(CalledOnValidThread()); |
604 if (!started_) | 602 |
| 603 TimeDelta poll = GetPollInterval(); |
| 604 bool rate_changed = !poll_timer_.IsRunning() || |
| 605 poll != poll_timer_.GetCurrentDelay(); |
| 606 |
| 607 if (type == FORCE_RESET) { |
| 608 last_poll_reset_ = base::TimeTicks::Now(); |
| 609 if (!rate_changed) |
| 610 poll_timer_.Reset(); |
| 611 } |
| 612 |
| 613 if (!rate_changed) |
605 return; | 614 return; |
606 | 615 |
607 TimeDelta poll_interval = GetPollInterval(); | 616 // Adjust poll rate. |
608 TimeDelta poll_delay = poll_interval; | 617 poll_timer_.Stop(); |
609 const TimeTicks now = TimeTicks::Now(); | 618 poll_timer_.Start(FROM_HERE, poll, this, |
610 | |
611 if (type == UPDATE_INTERVAL) { | |
612 // Override the delay based on the last successful poll time (if it was | |
613 // set). | |
614 TimeTicks new_poll_time; | |
615 if (!last_poll_reset_.is_null()) | |
616 new_poll_time = poll_interval + last_poll_reset_; | |
617 else | |
618 new_poll_time = poll_interval + now; | |
619 poll_delay = new_poll_time - now; | |
620 if (poll_delay < TimeDelta()) { | |
621 // The desired poll time was in the past, so trigger a poll now (the timer | |
622 /// will post the task asynchronously, so re-entrancy isn't an issue). | |
623 poll_delay = TimeDelta(); | |
624 } | |
625 } else { | |
626 // Otherwise just restart the timer. | |
627 DCHECK_EQ(FORCE_RESET, type); | |
628 DCHECK_EQ(GetPollInterval(), poll_delay); | |
629 last_poll_reset_ = now; | |
630 } | |
631 | |
632 SDVLOG(1) << "Updating polling delay to " << poll_delay.InMinutes() | |
633 << " minutes."; | |
634 | |
635 // Adjust poll rate. Start will reset the timer if it was already running. | |
636 poll_timer_.Start(FROM_HERE, poll_delay, this, | |
637 &SyncSchedulerImpl::PollTimerCallback); | 619 &SyncSchedulerImpl::PollTimerCallback); |
638 } | 620 } |
639 | 621 |
640 void SyncSchedulerImpl::RestartWaiting() { | 622 void SyncSchedulerImpl::RestartWaiting() { |
641 CHECK(wait_interval_.get()); | 623 CHECK(wait_interval_.get()); |
642 DCHECK(wait_interval_->length >= TimeDelta::FromSeconds(0)); | 624 DCHECK(wait_interval_->length >= TimeDelta::FromSeconds(0)); |
643 NotifyRetryTime(base::Time::Now() + wait_interval_->length); | 625 NotifyRetryTime(base::Time::Now() + wait_interval_->length); |
644 SDVLOG(2) << "Starting WaitInterval timer of length " | 626 SDVLOG(2) << "Starting WaitInterval timer of length " |
645 << wait_interval_->length.InMilliseconds() << "ms."; | 627 << wait_interval_->length.InMilliseconds() << "ms."; |
646 if (wait_interval_->mode == WaitInterval::THROTTLED) { | 628 if (wait_interval_->mode == WaitInterval::THROTTLED) { |
(...skipping 23 matching lines...) Expand all Loading... |
670 pending_wakeup_timer_.Stop(); | 652 pending_wakeup_timer_.Stop(); |
671 pending_configure_params_.reset(); | 653 pending_configure_params_.reset(); |
672 if (started_) | 654 if (started_) |
673 started_ = false; | 655 started_ = false; |
674 } | 656 } |
675 | 657 |
676 // This is the only place where we invoke DoSyncSessionJob with canary | 658 // This is the only place where we invoke DoSyncSessionJob with canary |
677 // privileges. Everyone else should use NORMAL_PRIORITY. | 659 // privileges. Everyone else should use NORMAL_PRIORITY. |
678 void SyncSchedulerImpl::TryCanaryJob() { | 660 void SyncSchedulerImpl::TryCanaryJob() { |
679 next_sync_session_job_priority_ = CANARY_PRIORITY; | 661 next_sync_session_job_priority_ = CANARY_PRIORITY; |
680 SDVLOG(2) << "Attempting canary job"; | |
681 TrySyncSessionJob(); | 662 TrySyncSessionJob(); |
682 } | 663 } |
683 | 664 |
684 void SyncSchedulerImpl::TrySyncSessionJob() { | 665 void SyncSchedulerImpl::TrySyncSessionJob() { |
685 // Post call to TrySyncSessionJobImpl on current thread. Later request for | 666 // Post call to TrySyncSessionJobImpl on current thread. Later request for |
686 // access token will be here. | 667 // access token will be here. |
687 base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind( | 668 base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind( |
688 &SyncSchedulerImpl::TrySyncSessionJobImpl, | 669 &SyncSchedulerImpl::TrySyncSessionJobImpl, |
689 weak_ptr_factory_.GetWeakPtr())); | 670 weak_ptr_factory_.GetWeakPtr())); |
690 } | 671 } |
691 | 672 |
692 void SyncSchedulerImpl::TrySyncSessionJobImpl() { | 673 void SyncSchedulerImpl::TrySyncSessionJobImpl() { |
693 JobPriority priority = next_sync_session_job_priority_; | 674 JobPriority priority = next_sync_session_job_priority_; |
694 next_sync_session_job_priority_ = NORMAL_PRIORITY; | 675 next_sync_session_job_priority_ = NORMAL_PRIORITY; |
695 | 676 |
696 nudge_tracker_.SetSyncCycleStartTime(base::TimeTicks::Now()); | 677 nudge_tracker_.SetSyncCycleStartTime(base::TimeTicks::Now()); |
697 | 678 |
698 DCHECK(CalledOnValidThread()); | 679 DCHECK(CalledOnValidThread()); |
699 if (mode_ == CONFIGURATION_MODE) { | 680 if (mode_ == CONFIGURATION_MODE) { |
700 if (pending_configure_params_) { | 681 if (pending_configure_params_) { |
701 SDVLOG(2) << "Found pending configure job"; | 682 SDVLOG(2) << "Found pending configure job"; |
702 DoConfigurationSyncSessionJob(priority); | 683 DoConfigurationSyncSessionJob(priority); |
703 } | 684 } |
704 } else if (CanRunNudgeJobNow(priority)) { | 685 } else if (CanRunNudgeJobNow(priority)) { |
705 if (nudge_tracker_.IsSyncRequired()) { | 686 if (nudge_tracker_.IsSyncRequired()) { |
706 SDVLOG(2) << "Found pending nudge job"; | 687 SDVLOG(2) << "Found pending nudge job"; |
707 DoNudgeSyncSessionJob(priority); | 688 DoNudgeSyncSessionJob(priority); |
708 } else if (((base::TimeTicks::Now() - last_poll_reset_) >= | 689 } else if (do_poll_after_credentials_updated_ || |
709 GetPollInterval())) { | 690 ((base::TimeTicks::Now() - last_poll_reset_) >= GetPollInterval())) { |
710 SDVLOG(2) << "Found pending poll"; | |
711 DoPollSyncSessionJob(); | 691 DoPollSyncSessionJob(); |
| 692 // Poll timer fires infrequently. Usually by this time access token is |
| 693 // already expired and poll job will fail with auth error. Set flag to |
| 694 // retry poll once ProfileSyncService gets new access token, TryCanaryJob |
| 695 // will be called after access token is retrieved. |
| 696 if (HttpResponse::SYNC_AUTH_ERROR == |
| 697 session_context_->connection_manager()->server_status()) { |
| 698 do_poll_after_credentials_updated_ = true; |
| 699 } |
712 } | 700 } |
713 } else { | 701 } |
714 // We must be in an error state. Transitioning out of each of these | 702 |
715 // error states should trigger a canary job. | 703 if (priority == CANARY_PRIORITY) { |
716 DCHECK(IsCurrentlyThrottled() || IsBackingOff() || | 704 // If this is canary job then whatever result was don't run poll job till |
717 session_context_->connection_manager()->HasInvalidAuthToken()); | 705 // the next time poll timer fires. |
| 706 do_poll_after_credentials_updated_ = false; |
718 } | 707 } |
719 | 708 |
720 if (IsBackingOff() && !pending_wakeup_timer_.IsRunning()) { | 709 if (IsBackingOff() && !pending_wakeup_timer_.IsRunning()) { |
721 // If we succeeded, our wait interval would have been cleared. If it hasn't | 710 // If we succeeded, our wait interval would have been cleared. If it hasn't |
722 // been cleared, then we should increase our backoff interval and schedule | 711 // been cleared, then we should increase our backoff interval and schedule |
723 // another retry. | 712 // another retry. |
724 TimeDelta length = delay_provider_->GetDelay(wait_interval_->length); | 713 TimeDelta length = delay_provider_->GetDelay(wait_interval_->length); |
725 wait_interval_.reset( | 714 wait_interval_.reset( |
726 new WaitInterval(WaitInterval::EXPONENTIAL_BACKOFF, length)); | 715 new WaitInterval(WaitInterval::EXPONENTIAL_BACKOFF, length)); |
727 SDVLOG(2) << "Sync cycle failed. Will back off for " | 716 SDVLOG(2) << "Sync cycle failed. Will back off for " |
728 << wait_interval_->length.InMilliseconds() << "ms."; | 717 << wait_interval_->length.InMilliseconds() << "ms."; |
729 RestartWaiting(); | 718 RestartWaiting(); |
730 } | 719 } |
731 } | 720 } |
732 | 721 |
733 void SyncSchedulerImpl::PollTimerCallback() { | 722 void SyncSchedulerImpl::PollTimerCallback() { |
734 DCHECK(CalledOnValidThread()); | 723 DCHECK(CalledOnValidThread()); |
735 CHECK(!syncer_->IsSyncing()); | 724 if (no_scheduling_allowed_) { |
| 725 // The no_scheduling_allowed_ flag is set by a function-scoped AutoReset in |
| 726 // functions that are called only on the sync thread. This function is also |
| 727 // called only on the sync thread, and only when it is posted by an expiring |
| 728 // timer. If we find that no_scheduling_allowed_ is set here, then |
| 729 // something is very wrong. Maybe someone mistakenly called us directly, or |
| 730 // mishandled the book-keeping for no_scheduling_allowed_. |
| 731 NOTREACHED() << "Illegal to schedule job while session in progress."; |
| 732 return; |
| 733 } |
736 | 734 |
737 TrySyncSessionJob(); | 735 TrySyncSessionJob(); |
738 } | 736 } |
739 | 737 |
740 void SyncSchedulerImpl::RetryTimerCallback() { | 738 void SyncSchedulerImpl::RetryTimerCallback() { |
741 TrySyncSessionJob(); | 739 TrySyncSessionJob(); |
742 } | 740 } |
743 | 741 |
744 void SyncSchedulerImpl::Unthrottle() { | 742 void SyncSchedulerImpl::Unthrottle() { |
745 DCHECK(CalledOnValidThread()); | 743 DCHECK(CalledOnValidThread()); |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
821 throttle_duration)); | 819 throttle_duration)); |
822 NotifyRetryTime(base::Time::Now() + wait_interval_->length); | 820 NotifyRetryTime(base::Time::Now() + wait_interval_->length); |
823 NotifyThrottledTypesChanged(ModelTypeSet::All()); | 821 NotifyThrottledTypesChanged(ModelTypeSet::All()); |
824 } | 822 } |
825 | 823 |
826 void SyncSchedulerImpl::OnTypesThrottled( | 824 void SyncSchedulerImpl::OnTypesThrottled( |
827 ModelTypeSet types, | 825 ModelTypeSet types, |
828 const base::TimeDelta& throttle_duration) { | 826 const base::TimeDelta& throttle_duration) { |
829 base::TimeTicks now = base::TimeTicks::Now(); | 827 base::TimeTicks now = base::TimeTicks::Now(); |
830 | 828 |
831 SDVLOG(1) << "Throttling " << ModelTypeSetToString(types) << " for " | |
832 << throttle_duration.InMinutes() << " minutes."; | |
833 | |
834 nudge_tracker_.SetTypesThrottledUntil(types, throttle_duration, now); | 829 nudge_tracker_.SetTypesThrottledUntil(types, throttle_duration, now); |
835 base::TimeDelta time_until_next_unthrottle = | 830 base::TimeDelta time_until_next_unthrottle = |
836 nudge_tracker_.GetTimeUntilNextUnthrottle(now); | 831 nudge_tracker_.GetTimeUntilNextUnthrottle(now); |
837 type_unthrottle_timer_.Start( | 832 type_unthrottle_timer_.Start( |
838 FROM_HERE, | 833 FROM_HERE, |
839 time_until_next_unthrottle, | 834 time_until_next_unthrottle, |
840 base::Bind(&SyncSchedulerImpl::TypeUnthrottle, | 835 base::Bind(&SyncSchedulerImpl::TypeUnthrottle, |
841 weak_ptr_factory_.GetWeakPtr(), | 836 weak_ptr_factory_.GetWeakPtr(), |
842 now + time_until_next_unthrottle)); | 837 now + time_until_next_unthrottle)); |
843 NotifyThrottledTypesChanged(nudge_tracker_.GetThrottledTypes()); | 838 NotifyThrottledTypesChanged(nudge_tracker_.GetThrottledTypes()); |
844 } | 839 } |
845 | 840 |
846 bool SyncSchedulerImpl::IsCurrentlyThrottled() { | 841 bool SyncSchedulerImpl::IsCurrentlyThrottled() { |
847 DCHECK(CalledOnValidThread()); | 842 DCHECK(CalledOnValidThread()); |
848 return wait_interval_.get() && wait_interval_->mode == | 843 return wait_interval_.get() && wait_interval_->mode == |
849 WaitInterval::THROTTLED; | 844 WaitInterval::THROTTLED; |
850 } | 845 } |
851 | 846 |
852 void SyncSchedulerImpl::OnReceivedShortPollIntervalUpdate( | 847 void SyncSchedulerImpl::OnReceivedShortPollIntervalUpdate( |
853 const base::TimeDelta& new_interval) { | 848 const base::TimeDelta& new_interval) { |
854 DCHECK(CalledOnValidThread()); | 849 DCHECK(CalledOnValidThread()); |
855 if (new_interval == syncer_short_poll_interval_seconds_) | |
856 return; | |
857 SDVLOG(1) << "Updating short poll interval to " << new_interval.InMinutes() | |
858 << " minutes."; | |
859 syncer_short_poll_interval_seconds_ = new_interval; | 850 syncer_short_poll_interval_seconds_ = new_interval; |
860 AdjustPolling(UPDATE_INTERVAL); | |
861 } | 851 } |
862 | 852 |
863 void SyncSchedulerImpl::OnReceivedLongPollIntervalUpdate( | 853 void SyncSchedulerImpl::OnReceivedLongPollIntervalUpdate( |
864 const base::TimeDelta& new_interval) { | 854 const base::TimeDelta& new_interval) { |
865 DCHECK(CalledOnValidThread()); | 855 DCHECK(CalledOnValidThread()); |
866 if (new_interval == syncer_long_poll_interval_seconds_) | |
867 return; | |
868 SDVLOG(1) << "Updating long poll interval to " << new_interval.InMinutes() | |
869 << " minutes."; | |
870 syncer_long_poll_interval_seconds_ = new_interval; | 856 syncer_long_poll_interval_seconds_ = new_interval; |
871 AdjustPolling(UPDATE_INTERVAL); | |
872 } | 857 } |
873 | 858 |
874 void SyncSchedulerImpl::OnReceivedCustomNudgeDelays( | 859 void SyncSchedulerImpl::OnReceivedCustomNudgeDelays( |
875 const std::map<ModelType, base::TimeDelta>& nudge_delays) { | 860 const std::map<ModelType, base::TimeDelta>& nudge_delays) { |
876 DCHECK(CalledOnValidThread()); | 861 DCHECK(CalledOnValidThread()); |
877 nudge_tracker_.OnReceivedCustomNudgeDelays(nudge_delays); | 862 nudge_tracker_.OnReceivedCustomNudgeDelays(nudge_delays); |
878 } | 863 } |
879 | 864 |
880 void SyncSchedulerImpl::OnReceivedClientInvalidationHintBufferSize(int size) { | 865 void SyncSchedulerImpl::OnReceivedClientInvalidationHintBufferSize(int size) { |
881 if (size > 0) | 866 if (size > 0) |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
922 | 907 |
923 #undef SDVLOG_LOC | 908 #undef SDVLOG_LOC |
924 | 909 |
925 #undef SDVLOG | 910 #undef SDVLOG |
926 | 911 |
927 #undef SLOG | 912 #undef SLOG |
928 | 913 |
929 #undef ENUM_CASE | 914 #undef ENUM_CASE |
930 | 915 |
931 } // namespace syncer | 916 } // namespace syncer |
OLD | NEW |