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