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" |
(...skipping 325 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
336 } | 336 } |
337 | 337 |
338 return true; | 338 return true; |
339 } | 339 } |
340 | 340 |
341 SyncSchedulerImpl::JobProcessDecision | 341 SyncSchedulerImpl::JobProcessDecision |
342 SyncSchedulerImpl::DecideWhileInWaitInterval(const SyncSessionJob& job, | 342 SyncSchedulerImpl::DecideWhileInWaitInterval(const SyncSessionJob& job, |
343 JobPriority priority) { | 343 JobPriority priority) { |
344 DCHECK_EQ(MessageLoop::current(), sync_loop_); | 344 DCHECK_EQ(MessageLoop::current(), sync_loop_); |
345 DCHECK(wait_interval_.get()); | 345 DCHECK(wait_interval_.get()); |
| 346 DCHECK_NE(job.purpose(), SyncSessionJob::POLL); |
346 | 347 |
347 SDVLOG(2) << "DecideWhileInWaitInterval with WaitInterval mode " | 348 SDVLOG(2) << "DecideWhileInWaitInterval with WaitInterval mode " |
348 << WaitInterval::GetModeString(wait_interval_->mode) | 349 << WaitInterval::GetModeString(wait_interval_->mode) |
349 << (wait_interval_->had_nudge ? " (had nudge)" : "") | 350 << (wait_interval_->had_nudge ? " (had nudge)" : "") |
350 << ((priority == CANARY_PRIORITY) ? " (canary)" : ""); | 351 << ((priority == CANARY_PRIORITY) ? " (canary)" : ""); |
351 | 352 |
352 if (job.purpose() == SyncSessionJob::POLL) | |
353 return DROP; | |
354 | |
355 // If we save a job while in a WaitInterval, there is a well-defined moment | 353 // If we save a job while in a WaitInterval, there is a well-defined moment |
356 // in time in the future when it makes sense for that SAVE-worthy job to try | 354 // in time in the future when it makes sense for that SAVE-worthy job to try |
357 // running again -- the end of the WaitInterval. | 355 // running again -- the end of the WaitInterval. |
358 DCHECK(job.purpose() == SyncSessionJob::NUDGE || | 356 DCHECK(job.purpose() == SyncSessionJob::NUDGE || |
359 job.purpose() == SyncSessionJob::CONFIGURATION); | 357 job.purpose() == SyncSessionJob::CONFIGURATION); |
360 | 358 |
361 // If throttled, there's a clock ticking to unthrottle. We want to get | 359 // If throttled, there's a clock ticking to unthrottle. We want to get |
362 // on the same train. | 360 // on the same train. |
363 if (wait_interval_->mode == WaitInterval::THROTTLED) | 361 if (wait_interval_->mode == WaitInterval::THROTTLED) |
364 return SAVE; | 362 return SAVE; |
(...skipping 11 matching lines...) Expand all Loading... |
376 return CONTINUE; | 374 return CONTINUE; |
377 } | 375 } |
378 return (priority == CANARY_PRIORITY) ? CONTINUE : SAVE; | 376 return (priority == CANARY_PRIORITY) ? CONTINUE : SAVE; |
379 } | 377 } |
380 | 378 |
381 SyncSchedulerImpl::JobProcessDecision SyncSchedulerImpl::DecideOnJob( | 379 SyncSchedulerImpl::JobProcessDecision SyncSchedulerImpl::DecideOnJob( |
382 const SyncSessionJob& job, | 380 const SyncSessionJob& job, |
383 JobPriority priority) { | 381 JobPriority priority) { |
384 DCHECK_EQ(MessageLoop::current(), sync_loop_); | 382 DCHECK_EQ(MessageLoop::current(), sync_loop_); |
385 | 383 |
| 384 // POLL jobs do not call this function. |
| 385 DCHECK(job.purpose() == SyncSessionJob::NUDGE || |
| 386 job.purpose() == SyncSessionJob::CONFIGURATION); |
| 387 |
386 // See if our type is throttled. | 388 // See if our type is throttled. |
387 ModelTypeSet throttled_types = | 389 ModelTypeSet throttled_types = |
388 session_context_->throttled_data_type_tracker()->GetThrottledTypes(); | 390 session_context_->throttled_data_type_tracker()->GetThrottledTypes(); |
389 if (job.purpose() == SyncSessionJob::NUDGE && | 391 if (job.purpose() == SyncSessionJob::NUDGE && |
390 job.session()->source().updates_source == GetUpdatesCallerInfo::LOCAL) { | 392 job.session()->source().updates_source == GetUpdatesCallerInfo::LOCAL) { |
391 ModelTypeSet requested_types; | 393 ModelTypeSet requested_types; |
392 for (ModelTypeInvalidationMap::const_iterator i = | 394 for (ModelTypeInvalidationMap::const_iterator i = |
393 job.session()->source().types.begin(); | 395 job.session()->source().types.begin(); |
394 i != job.session()->source().types.end(); | 396 i != job.session()->source().types.end(); |
395 ++i) { | 397 ++i) { |
396 requested_types.Put(i->first); | 398 requested_types.Put(i->first); |
397 } | 399 } |
398 | 400 |
399 // If all types are throttled, do not CONTINUE. Today, we don't treat | 401 // If all types are throttled, do not CONTINUE. Today, we don't treat |
400 // a per-datatype "unthrottle" event as something that should force a | 402 // a per-datatype "unthrottle" event as something that should force a |
401 // canary job. For this reason, there's no good time to reschedule this job | 403 // canary job. For this reason, there's no good time to reschedule this job |
402 // to run -- we'll lazily wait for an independent event to trigger a sync. | 404 // to run -- we'll lazily wait for an independent event to trigger a sync. |
403 // Note that there may already be such an event if we're in a WaitInterval, | 405 // Note that there may already be such an event if we're in a WaitInterval, |
404 // so we can retry it then. | 406 // so we can retry it then. |
405 if (!requested_types.Empty() && throttled_types.HasAll(requested_types)) | 407 if (!requested_types.Empty() && throttled_types.HasAll(requested_types)) |
406 return DROP; // TODO(tim): Don't drop. http://crbug.com/177659 | 408 return DROP; // TODO(tim): Don't drop. http://crbug.com/177659 |
407 } | 409 } |
408 | 410 |
409 if (wait_interval_.get()) | 411 if (wait_interval_.get()) |
410 return DecideWhileInWaitInterval(job, priority); | 412 return DecideWhileInWaitInterval(job, priority); |
411 | 413 |
412 if (mode_ == CONFIGURATION_MODE) { | 414 if (mode_ == CONFIGURATION_MODE) { |
413 if (job.purpose() == SyncSessionJob::NUDGE) | 415 if (job.purpose() == SyncSessionJob::NUDGE) |
414 return SAVE; // Running requires a mode switch. | 416 return SAVE; // Running requires a mode switch. |
415 else if (job.purpose() == SyncSessionJob::CONFIGURATION) | 417 else // Implies job.purpose() == SyncSessionJob::CONFIGURATION. |
416 return CONTINUE; | 418 return CONTINUE; |
417 else | |
418 return DROP; | |
419 } | 419 } |
420 | 420 |
421 // We are in normal mode. | 421 // We are in normal mode. |
422 DCHECK_EQ(mode_, NORMAL_MODE); | 422 DCHECK_EQ(mode_, NORMAL_MODE); |
423 DCHECK_NE(job.purpose(), SyncSessionJob::CONFIGURATION); | 423 DCHECK_NE(job.purpose(), SyncSessionJob::CONFIGURATION); |
424 | 424 |
425 // Note about some subtle scheduling semantics. | 425 // Note about some subtle scheduling semantics. |
426 // | 426 // |
427 // It's possible at this point that |job| is known to be unnecessary, and | 427 // It's possible at this point that |job| is known to be unnecessary, and |
428 // dropping it would be perfectly safe and correct. Consider | 428 // dropping it would be perfectly safe and correct. Consider |
429 // | 429 // |
430 // 1) |job| is a POLL with a |scheduled_start| time that is less than | 430 // 1) |job| is a NUDGE (for any combination of types) with a |
431 // the time that the last successful all-datatype NUDGE completed. | |
432 // | |
433 // 2) |job| is a NUDGE (for any combination of types) with a | |
434 // |scheduled_start| time that is less than the time that the last | 431 // |scheduled_start| time that is less than the time that the last |
435 // successful all-datatype NUDGE completed, and it has a NOTIFICATION | 432 // successful all-datatype NUDGE completed, and it has a NOTIFICATION |
436 // GetUpdatesCallerInfo value yet offers no new notification hint. | 433 // GetUpdatesCallerInfo value yet offers no new notification hint. |
437 // | 434 // |
438 // 3) |job| is a NUDGE with a |scheduled_start| time that is less than | 435 // 2) |job| is a NUDGE with a |scheduled_start| time that is less than |
439 // the time that the last successful matching-datatype NUDGE completed, | 436 // the time that the last successful matching-datatype NUDGE completed, |
440 // and payloads (hints) are identical to that last successful NUDGE. | 437 // and payloads (hints) are identical to that last successful NUDGE. |
441 // | 438 // |
442 // Case 1 can occur if the POLL timer fires *after* a call to | 439 // We avoid cases 1 and 2 by externally synchronizing NUDGE requests -- |
443 // ScheduleSyncSessionJob for a NUDGE, but *before* the thread actually | |
444 // picks the resulting posted task off of the MessageLoop. The NUDGE will | |
445 // run first and complete at a time greater than the POLL scheduled_start. | |
446 // However, this case (and POLLs in general) is so rare that we ignore it ( | |
447 // and avoid the required bookeeping to simplify code). | |
448 // | |
449 // We avoid cases 2 and 3 by externally synchronizing NUDGE requests -- | |
450 // scheduling a NUDGE requires command of the sync thread, which is | 440 // scheduling a NUDGE requires command of the sync thread, which is |
451 // impossible* from outside of SyncScheduler if a NUDGE is taking place. | 441 // impossible* from outside of SyncScheduler if a NUDGE is taking place. |
452 // And if you have command of the sync thread when scheduling a NUDGE and a | 442 // And if you have command of the sync thread when scheduling a NUDGE and a |
453 // previous NUDGE exists, they will be coalesced and the stale job will be | 443 // previous NUDGE exists, they will be coalesced and the stale job will be |
454 // cancelled via the session-equality check in DoSyncSessionJob. | 444 // cancelled via the session-equality check in DoSyncSessionJob. |
455 // | 445 // |
456 // * It's not strictly "impossible", but it would be reentrant and hence | 446 // * It's not strictly "impossible", but it would be reentrant and hence |
457 // illegal. e.g. scheduling a job and re-entering the SyncScheduler is NOT a | 447 // illegal. e.g. scheduling a job and re-entering the SyncScheduler is NOT a |
458 // legal side effect of any of the work being done as part of a sync cycle. | 448 // legal side effect of any of the work being done as part of a sync cycle. |
459 // See |no_scheduling_allowed_| for details. | 449 // See |no_scheduling_allowed_| for details. |
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
557 << "source " << GetNudgeSourceString(source) << ", " | 547 << "source " << GetNudgeSourceString(source) << ", " |
558 << "payloads " | 548 << "payloads " |
559 << ModelTypeInvalidationMapToString(invalidation_map); | 549 << ModelTypeInvalidationMapToString(invalidation_map); |
560 | 550 |
561 SyncSchedulerImpl::ScheduleNudgeImpl(desired_delay, | 551 SyncSchedulerImpl::ScheduleNudgeImpl(desired_delay, |
562 GetUpdatesFromNudgeSource(source), | 552 GetUpdatesFromNudgeSource(source), |
563 invalidation_map, | 553 invalidation_map, |
564 nudge_location); | 554 nudge_location); |
565 } | 555 } |
566 | 556 |
| 557 |
| 558 // TODO(zea): Consider adding separate throttling/backoff for datatype |
| 559 // refresh requests. |
567 void SyncSchedulerImpl::ScheduleNudgeImpl( | 560 void SyncSchedulerImpl::ScheduleNudgeImpl( |
568 const TimeDelta& delay, | 561 const TimeDelta& delay, |
569 GetUpdatesCallerInfo::GetUpdatesSource source, | 562 GetUpdatesCallerInfo::GetUpdatesSource source, |
570 const ModelTypeInvalidationMap& invalidation_map, | 563 const ModelTypeInvalidationMap& invalidation_map, |
571 const tracked_objects::Location& nudge_location) { | 564 const tracked_objects::Location& nudge_location) { |
572 DCHECK_EQ(MessageLoop::current(), sync_loop_); | 565 DCHECK_EQ(MessageLoop::current(), sync_loop_); |
573 DCHECK(!invalidation_map.empty()) << "Nudge scheduled for no types!"; | 566 DCHECK(!invalidation_map.empty()) << "Nudge scheduled for no types!"; |
574 | 567 |
| 568 if (no_scheduling_allowed_) { |
| 569 NOTREACHED() << "Illegal to schedule job while session in progress."; |
| 570 return; |
| 571 } |
| 572 |
575 if (!started_) { | 573 if (!started_) { |
576 SDVLOG_LOC(nudge_location, 2) | 574 SDVLOG_LOC(nudge_location, 2) |
577 << "Dropping nudge, scheduler is not running."; | 575 << "Dropping nudge, scheduler is not running."; |
578 return; | 576 return; |
579 } | 577 } |
580 | 578 |
581 SDVLOG_LOC(nudge_location, 2) | 579 SDVLOG_LOC(nudge_location, 2) |
582 << "In ScheduleNudgeImpl with delay " | 580 << "In ScheduleNudgeImpl with delay " |
583 << delay.InMilliseconds() << " ms, " | 581 << delay.InMilliseconds() << " ms, " |
584 << "source " << GetUpdatesSourceString(source) << ", " | 582 << "source " << GetUpdatesSourceString(source) << ", " |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
624 // was previously unscheduled and giving it wings, so take care to reset | 622 // was previously unscheduled and giving it wings, so take care to reset |
625 // unscheduled nudge storage. | 623 // unscheduled nudge storage. |
626 job = pending_nudge_->CloneAndAbandon(); | 624 job = pending_nudge_->CloneAndAbandon(); |
627 pending_nudge_ = NULL; | 625 pending_nudge_ = NULL; |
628 unscheduled_nudge_storage_.reset(); | 626 unscheduled_nudge_storage_.reset(); |
629 // It's also possible we took a canary job, since we allow one nudge | 627 // It's also possible we took a canary job, since we allow one nudge |
630 // per backoff interval. | 628 // per backoff interval. |
631 DCHECK(!wait_interval_ || !wait_interval_->had_nudge); | 629 DCHECK(!wait_interval_ || !wait_interval_->had_nudge); |
632 } | 630 } |
633 | 631 |
634 // TODO(zea): Consider adding separate throttling/backoff for datatype | 632 TimeDelta run_delay = job->scheduled_start() - TimeTicks::Now(); |
635 // refresh requests. | 633 if (run_delay < TimeDelta::FromMilliseconds(0)) |
636 ScheduleSyncSessionJob(nudge_location, job.Pass()); | 634 run_delay = TimeDelta::FromMilliseconds(0); |
| 635 SDVLOG_LOC(nudge_location, 2) |
| 636 << "Scheduling a nudge with " |
| 637 << run_delay.InMilliseconds() << " ms delay"; |
| 638 |
| 639 pending_nudge_ = job.get(); |
| 640 PostDelayedTask(nudge_location, "DoSyncSessionJob", |
| 641 base::Bind(base::IgnoreResult(&SyncSchedulerImpl::DoSyncSessionJob), |
| 642 weak_ptr_factory_.GetWeakPtr(), |
| 643 base::Passed(&job), |
| 644 NORMAL_PRIORITY), |
| 645 run_delay); |
637 } | 646 } |
638 | 647 |
639 const char* SyncSchedulerImpl::GetModeString(SyncScheduler::Mode mode) { | 648 const char* SyncSchedulerImpl::GetModeString(SyncScheduler::Mode mode) { |
640 switch (mode) { | 649 switch (mode) { |
641 ENUM_CASE(CONFIGURATION_MODE); | 650 ENUM_CASE(CONFIGURATION_MODE); |
642 ENUM_CASE(NORMAL_MODE); | 651 ENUM_CASE(NORMAL_MODE); |
643 } | 652 } |
644 return ""; | 653 return ""; |
645 } | 654 } |
646 | 655 |
(...skipping 25 matching lines...) Expand all Loading... |
672 SDVLOG_LOC(from_here, 3) << "Posting " << name << " task with " | 681 SDVLOG_LOC(from_here, 3) << "Posting " << name << " task with " |
673 << delay.InMilliseconds() << " ms delay"; | 682 << delay.InMilliseconds() << " ms delay"; |
674 DCHECK_EQ(MessageLoop::current(), sync_loop_); | 683 DCHECK_EQ(MessageLoop::current(), sync_loop_); |
675 if (!started_) { | 684 if (!started_) { |
676 SDVLOG(1) << "Not posting task as scheduler is stopped."; | 685 SDVLOG(1) << "Not posting task as scheduler is stopped."; |
677 return; | 686 return; |
678 } | 687 } |
679 sync_loop_->PostDelayedTask(from_here, task, delay); | 688 sync_loop_->PostDelayedTask(from_here, task, delay); |
680 } | 689 } |
681 | 690 |
682 void SyncSchedulerImpl::ScheduleSyncSessionJob( | |
683 const tracked_objects::Location& loc, | |
684 scoped_ptr<SyncSessionJob> job) { | |
685 DCHECK_EQ(MessageLoop::current(), sync_loop_); | |
686 if (no_scheduling_allowed_) { | |
687 NOTREACHED() << "Illegal to schedule job while session in progress."; | |
688 return; | |
689 } | |
690 | |
691 TimeDelta delay = job->scheduled_start() - TimeTicks::Now(); | |
692 if (delay < TimeDelta::FromMilliseconds(0)) | |
693 delay = TimeDelta::FromMilliseconds(0); | |
694 SDVLOG_LOC(loc, 2) | |
695 << "In ScheduleSyncSessionJob with " | |
696 << SyncSessionJob::GetPurposeString(job->purpose()) | |
697 << " job and " << delay.InMilliseconds() << " ms delay"; | |
698 | |
699 DCHECK(job->purpose() == SyncSessionJob::NUDGE || | |
700 job->purpose() == SyncSessionJob::POLL); | |
701 if (job->purpose() == SyncSessionJob::NUDGE) { | |
702 SDVLOG_LOC(loc, 2) << "Resetting pending_nudge to "; | |
703 DCHECK(!pending_nudge_ || pending_nudge_->session() == | |
704 job->session()); | |
705 pending_nudge_ = job.get(); | |
706 } | |
707 | |
708 PostDelayedTask(loc, "DoSyncSessionJob", | |
709 base::Bind(base::IgnoreResult(&SyncSchedulerImpl::DoSyncSessionJob), | |
710 weak_ptr_factory_.GetWeakPtr(), | |
711 base::Passed(&job), | |
712 NORMAL_PRIORITY), | |
713 delay); | |
714 } | |
715 | |
716 bool SyncSchedulerImpl::DoSyncSessionJob(scoped_ptr<SyncSessionJob> job, | 691 bool SyncSchedulerImpl::DoSyncSessionJob(scoped_ptr<SyncSessionJob> job, |
717 JobPriority priority) { | 692 JobPriority priority) { |
718 DCHECK_EQ(MessageLoop::current(), sync_loop_); | 693 DCHECK_EQ(MessageLoop::current(), sync_loop_); |
719 if (job->purpose() == SyncSessionJob::NUDGE) { | 694 if (job->purpose() == SyncSessionJob::NUDGE) { |
720 if (pending_nudge_ == NULL || | 695 if (pending_nudge_ == NULL || |
721 pending_nudge_->session() != job->session()) { | 696 pending_nudge_->session() != job->session()) { |
722 // |job| is abandoned. | 697 // |job| is abandoned. |
723 SDVLOG(2) << "Dropping a nudge in " | 698 SDVLOG(2) << "Dropping a nudge in " |
724 << "DoSyncSessionJob because another nudge was scheduled"; | 699 << "DoSyncSessionJob because another nudge was scheduled"; |
725 return false; | 700 return false; |
(...skipping 23 matching lines...) Expand all Loading... |
749 return false; | 724 return false; |
750 } | 725 } |
751 | 726 |
752 SDVLOG(2) << "Calling SyncShare with " | 727 SDVLOG(2) << "Calling SyncShare with " |
753 << SyncSessionJob::GetPurposeString(job->purpose()) << " job"; | 728 << SyncSessionJob::GetPurposeString(job->purpose()) << " job"; |
754 bool premature_exit = !syncer_->SyncShare(job->mutable_session(), | 729 bool premature_exit = !syncer_->SyncShare(job->mutable_session(), |
755 job->start_step(), | 730 job->start_step(), |
756 job->end_step()); | 731 job->end_step()); |
757 SDVLOG(2) << "Done SyncShare, returned: " << premature_exit; | 732 SDVLOG(2) << "Done SyncShare, returned: " << premature_exit; |
758 | 733 |
759 return FinishSyncSessionJob(job.Pass(), premature_exit); | 734 bool success = FinishSyncSessionJob(job.get(), premature_exit); |
| 735 |
| 736 if (IsSyncingCurrentlySilenced()) { |
| 737 SDVLOG(2) << "We are currently throttled; scheduling Unthrottle."; |
| 738 // If we're here, it's because |job| was silenced until a server specified |
| 739 // time. (Note, it had to be |job|, because DecideOnJob would not permit |
| 740 // any job through while in WaitInterval::THROTTLED). |
| 741 scoped_ptr<SyncSessionJob> clone = job->Clone(); |
| 742 if (clone->purpose() == SyncSessionJob::NUDGE) |
| 743 pending_nudge_ = clone.get(); |
| 744 else if (clone->purpose() == SyncSessionJob::CONFIGURATION) |
| 745 wait_interval_->pending_configure_job = clone.get(); |
| 746 else |
| 747 NOTREACHED(); |
| 748 |
| 749 RestartWaiting(clone.Pass()); |
| 750 return success; |
| 751 } |
| 752 |
| 753 if (!success) |
| 754 ScheduleNextSync(job.Pass()); |
| 755 |
| 756 return success; |
| 757 } |
| 758 |
| 759 bool SyncSchedulerImpl::ShouldPoll() { |
| 760 if (wait_interval_.get()) { |
| 761 SDVLOG(2) << "Not running poll in wait interval."; |
| 762 return false; |
| 763 } |
| 764 |
| 765 if (mode_ == CONFIGURATION_MODE) { |
| 766 SDVLOG(2) << "Not running poll in configuration mode."; |
| 767 return false; |
| 768 } |
| 769 |
| 770 // TODO(rlarocque): Refactor decision-making logic common to all types |
| 771 // of jobs into a shared function. |
| 772 |
| 773 if (session_context_->connection_manager()->HasInvalidAuthToken()) { |
| 774 SDVLOG(2) << "Not running poll because auth token is invalid."; |
| 775 return false; |
| 776 } |
| 777 |
| 778 return true; |
| 779 } |
| 780 |
| 781 void SyncSchedulerImpl::DoPollSyncSessionJob(scoped_ptr<SyncSessionJob> job) { |
| 782 DCHECK_EQ(job->purpose(), SyncSessionJob::POLL); |
| 783 |
| 784 base::AutoReset<bool> protector(&no_scheduling_allowed_, true); |
| 785 |
| 786 if (!ShouldPoll()) |
| 787 return; |
| 788 |
| 789 SDVLOG(2) << "Calling SyncShare with " |
| 790 << SyncSessionJob::GetPurposeString(job->purpose()) << " job"; |
| 791 bool premature_exit = !syncer_->SyncShare(job->mutable_session(), |
| 792 job->start_step(), |
| 793 job->end_step()); |
| 794 SDVLOG(2) << "Done SyncShare, returned: " << premature_exit; |
| 795 |
| 796 FinishSyncSessionJob(job.get(), premature_exit); |
| 797 |
| 798 if (IsSyncingCurrentlySilenced()) { |
| 799 // This will start the countdown to unthrottle. Other kinds of jobs would |
| 800 // schedule themselves as the post-unthrottle canary. A poll job is not |
| 801 // that urgent, so it does not get to be the canary. We still need to start |
| 802 // the timer regardless. Otherwise there could be no one to clear the |
| 803 // WaitInterval when the throttling expires. |
| 804 RestartWaiting(scoped_ptr<SyncSessionJob>()); |
| 805 } |
760 } | 806 } |
761 | 807 |
762 void SyncSchedulerImpl::UpdateNudgeTimeRecords(const SyncSourceInfo& info) { | 808 void SyncSchedulerImpl::UpdateNudgeTimeRecords(const SyncSourceInfo& info) { |
763 DCHECK_EQ(MessageLoop::current(), sync_loop_); | 809 DCHECK_EQ(MessageLoop::current(), sync_loop_); |
764 | 810 |
765 // We are interested in recording time between local nudges for datatypes. | 811 // We are interested in recording time between local nudges for datatypes. |
766 // TODO(tim): Consider tracking LOCAL_NOTIFICATION as well. | 812 // TODO(tim): Consider tracking LOCAL_NOTIFICATION as well. |
767 if (info.updates_source != GetUpdatesCallerInfo::LOCAL) | 813 if (info.updates_source != GetUpdatesCallerInfo::LOCAL) |
768 return; | 814 return; |
769 | 815 |
770 base::TimeTicks now = TimeTicks::Now(); | 816 base::TimeTicks now = TimeTicks::Now(); |
771 // Update timing information for how often datatypes are triggering nudges. | 817 // Update timing information for how often datatypes are triggering nudges. |
772 for (ModelTypeInvalidationMap::const_iterator iter = info.types.begin(); | 818 for (ModelTypeInvalidationMap::const_iterator iter = info.types.begin(); |
773 iter != info.types.end(); | 819 iter != info.types.end(); |
774 ++iter) { | 820 ++iter) { |
775 base::TimeTicks previous = last_local_nudges_by_model_type_[iter->first]; | 821 base::TimeTicks previous = last_local_nudges_by_model_type_[iter->first]; |
776 last_local_nudges_by_model_type_[iter->first] = now; | 822 last_local_nudges_by_model_type_[iter->first] = now; |
777 if (previous.is_null()) | 823 if (previous.is_null()) |
778 continue; | 824 continue; |
779 | 825 |
780 #define PER_DATA_TYPE_MACRO(type_str) \ | 826 #define PER_DATA_TYPE_MACRO(type_str) \ |
781 SYNC_FREQ_HISTOGRAM("Sync.Freq" type_str, now - previous); | 827 SYNC_FREQ_HISTOGRAM("Sync.Freq" type_str, now - previous); |
782 SYNC_DATA_TYPE_HISTOGRAM(iter->first); | 828 SYNC_DATA_TYPE_HISTOGRAM(iter->first); |
783 #undef PER_DATA_TYPE_MACRO | 829 #undef PER_DATA_TYPE_MACRO |
784 } | 830 } |
785 } | 831 } |
786 | 832 |
787 bool SyncSchedulerImpl::FinishSyncSessionJob(scoped_ptr<SyncSessionJob> job, | 833 bool SyncSchedulerImpl::FinishSyncSessionJob(SyncSessionJob* job, |
788 bool exited_prematurely) { | 834 bool exited_prematurely) { |
789 DCHECK_EQ(MessageLoop::current(), sync_loop_); | 835 DCHECK_EQ(MessageLoop::current(), sync_loop_); |
790 | 836 |
791 // Let job know that we're through syncing (calling SyncShare) at this point. | 837 // Let job know that we're through syncing (calling SyncShare) at this point. |
792 bool succeeded = false; | 838 bool succeeded = false; |
793 { | 839 { |
794 base::AutoReset<bool> protector(&no_scheduling_allowed_, true); | 840 base::AutoReset<bool> protector(&no_scheduling_allowed_, true); |
795 succeeded = job->Finish(exited_prematurely); | 841 succeeded = job->Finish(exited_prematurely); |
796 } | 842 } |
797 | 843 |
798 SDVLOG(2) << "Updating the next polling time after SyncMain"; | 844 SDVLOG(2) << "Updating the next polling time after SyncMain"; |
799 ScheduleNextSync(job.Pass(), succeeded); | |
800 return succeeded; | |
801 } | |
802 | 845 |
803 void SyncSchedulerImpl::ScheduleNextSync( | 846 AdjustPolling(job); |
804 scoped_ptr<SyncSessionJob> finished_job, bool succeeded) { | |
805 DCHECK_EQ(MessageLoop::current(), sync_loop_); | |
806 | |
807 AdjustPolling(finished_job.get()); | |
808 | 847 |
809 if (succeeded) { | 848 if (succeeded) { |
810 // No job currently supported by the scheduler could succeed without | 849 // No job currently supported by the scheduler could succeed without |
811 // successfully reaching the server. Therefore, if we make it here, it is | 850 // successfully reaching the server. Therefore, if we make it here, it is |
812 // appropriate to reset the backoff interval. | 851 // appropriate to reset the backoff interval. |
813 wait_interval_.reset(); | 852 wait_interval_.reset(); |
814 NotifyRetryTime(base::Time()); | 853 NotifyRetryTime(base::Time()); |
815 SDVLOG(2) << "Job succeeded so not scheduling more jobs"; | 854 SDVLOG(2) << "Job succeeded so not scheduling more jobs"; |
816 return; | |
817 } | 855 } |
818 | 856 |
819 if (IsSyncingCurrentlySilenced()) { | 857 return succeeded; |
820 SDVLOG(2) << "We are currently throttled; scheduling Unthrottle."; | 858 } |
821 // If we're here, it's because |job| was silenced until a server specified | |
822 // time. (Note, it had to be |job|, because DecideOnJob would not permit | |
823 // any job through while in WaitInterval::THROTTLED). | |
824 scoped_ptr<SyncSessionJob> clone = finished_job->Clone(); | |
825 if (clone->purpose() == SyncSessionJob::NUDGE) | |
826 pending_nudge_ = clone.get(); | |
827 else if (clone->purpose() == SyncSessionJob::CONFIGURATION) | |
828 wait_interval_->pending_configure_job = clone.get(); | |
829 else | |
830 clone.reset(); // Unthrottling is enough, no need to force a canary. | |
831 | 859 |
832 RestartWaiting(clone.Pass()); | 860 void SyncSchedulerImpl::ScheduleNextSync( |
833 return; | 861 scoped_ptr<SyncSessionJob> finished_job) { |
834 } | 862 DCHECK_EQ(MessageLoop::current(), sync_loop_); |
835 | 863 DCHECK(finished_job->purpose() == SyncSessionJob::CONFIGURATION |
836 if (finished_job->purpose() == SyncSessionJob::POLL) { | 864 || finished_job->purpose() == SyncSessionJob::NUDGE); |
837 return; // We don't retry POLL jobs. | |
838 } | |
839 | 865 |
840 // TODO(rlarocque): There's no reason why we should blindly backoff and retry | 866 // TODO(rlarocque): There's no reason why we should blindly backoff and retry |
841 // if we don't succeed. Some types of errors are not likely to disappear on | 867 // if we don't succeed. Some types of errors are not likely to disappear on |
842 // their own. With the return values now available in the old_job.session, | 868 // their own. With the return values now available in the old_job.session, |
843 // we should be able to detect such errors and only retry when we detect | 869 // we should be able to detect such errors and only retry when we detect |
844 // transient errors. | 870 // transient errors. |
845 | 871 |
846 if (IsBackingOff() && wait_interval_->timer.IsRunning() && | 872 if (IsBackingOff() && wait_interval_->timer.IsRunning() && |
847 mode_ == NORMAL_MODE) { | 873 mode_ == NORMAL_MODE) { |
848 // When in normal mode, we allow up to one nudge per backoff interval. It | 874 // When in normal mode, we allow up to one nudge per backoff interval. It |
(...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
973 void SyncSchedulerImpl::DoCanaryJob(scoped_ptr<SyncSessionJob> to_be_canary) { | 999 void SyncSchedulerImpl::DoCanaryJob(scoped_ptr<SyncSessionJob> to_be_canary) { |
974 DCHECK_EQ(MessageLoop::current(), sync_loop_); | 1000 DCHECK_EQ(MessageLoop::current(), sync_loop_); |
975 SDVLOG(2) << "Do canary job"; | 1001 SDVLOG(2) << "Do canary job"; |
976 | 1002 |
977 if (to_be_canary->purpose() == SyncSessionJob::NUDGE) { | 1003 if (to_be_canary->purpose() == SyncSessionJob::NUDGE) { |
978 // TODO(tim): Bug 158313. Remove this check. | 1004 // TODO(tim): Bug 158313. Remove this check. |
979 if (pending_nudge_ == NULL || | 1005 if (pending_nudge_ == NULL || |
980 pending_nudge_->session() != to_be_canary->session()) { | 1006 pending_nudge_->session() != to_be_canary->session()) { |
981 // |job| is abandoned. | 1007 // |job| is abandoned. |
982 SDVLOG(2) << "Dropping a nudge in " | 1008 SDVLOG(2) << "Dropping a nudge in " |
983 << "DoSyncSessionJob because another nudge was scheduled"; | 1009 << "DoCanaryJob because another nudge was scheduled"; |
984 return; | 1010 return; |
985 } | 1011 } |
986 DCHECK_EQ(pending_nudge_->session(), to_be_canary->session()); | 1012 DCHECK_EQ(pending_nudge_->session(), to_be_canary->session()); |
987 } | 1013 } |
988 | 1014 |
989 // This is the only place where we invoke DoSyncSessionJob with canary | 1015 // This is the only place where we invoke DoSyncSessionJob with canary |
990 // privileges. Everyone else should use NORMAL_PRIORITY. | 1016 // privileges. Everyone else should use NORMAL_PRIORITY. |
991 DoSyncSessionJob(to_be_canary.Pass(), CANARY_PRIORITY); | 1017 DoSyncSessionJob(to_be_canary.Pass(), CANARY_PRIORITY); |
992 } | 1018 } |
993 | 1019 |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1028 DCHECK_EQ(MessageLoop::current(), sync_loop_); | 1054 DCHECK_EQ(MessageLoop::current(), sync_loop_); |
1029 ModelSafeRoutingInfo r; | 1055 ModelSafeRoutingInfo r; |
1030 ModelTypeInvalidationMap invalidation_map = | 1056 ModelTypeInvalidationMap invalidation_map = |
1031 ModelSafeRoutingInfoToInvalidationMap(r, std::string()); | 1057 ModelSafeRoutingInfoToInvalidationMap(r, std::string()); |
1032 SyncSourceInfo info(GetUpdatesCallerInfo::PERIODIC, invalidation_map); | 1058 SyncSourceInfo info(GetUpdatesCallerInfo::PERIODIC, invalidation_map); |
1033 scoped_ptr<SyncSession> s(CreateSyncSession(info)); | 1059 scoped_ptr<SyncSession> s(CreateSyncSession(info)); |
1034 scoped_ptr<SyncSessionJob> job(new SyncSessionJob(SyncSessionJob::POLL, | 1060 scoped_ptr<SyncSessionJob> job(new SyncSessionJob(SyncSessionJob::POLL, |
1035 TimeTicks::Now(), | 1061 TimeTicks::Now(), |
1036 s.Pass(), | 1062 s.Pass(), |
1037 ConfigurationParams())); | 1063 ConfigurationParams())); |
1038 ScheduleSyncSessionJob(FROM_HERE, job.Pass()); | 1064 if (no_scheduling_allowed_) { |
| 1065 // The no_scheduling_allowed_ flag is set by a function-scoped AutoReset in |
| 1066 // functions that are called only on the sync thread. This function is also |
| 1067 // called only on the sync thread, and only when it is posted by an expiring |
| 1068 // timer. If we find that no_scheduling_allowed_ is set here, then |
| 1069 // something is very wrong. Maybe someone mistakenly called us directly, or |
| 1070 // mishandled the book-keeping for no_scheduling_allowed_. |
| 1071 NOTREACHED() << "Illegal to schedule job while session in progress."; |
| 1072 return; |
| 1073 } |
| 1074 |
| 1075 DoPollSyncSessionJob(job.Pass()); |
1039 } | 1076 } |
1040 | 1077 |
1041 void SyncSchedulerImpl::Unthrottle(scoped_ptr<SyncSessionJob> to_be_canary) { | 1078 void SyncSchedulerImpl::Unthrottle(scoped_ptr<SyncSessionJob> to_be_canary) { |
1042 DCHECK_EQ(MessageLoop::current(), sync_loop_); | 1079 DCHECK_EQ(MessageLoop::current(), sync_loop_); |
1043 DCHECK_EQ(WaitInterval::THROTTLED, wait_interval_->mode); | 1080 DCHECK_EQ(WaitInterval::THROTTLED, wait_interval_->mode); |
1044 DCHECK(!to_be_canary.get() || pending_nudge_ == to_be_canary.get() || | 1081 DCHECK(!to_be_canary.get() || pending_nudge_ == to_be_canary.get() || |
1045 wait_interval_->pending_configure_job == to_be_canary.get()); | 1082 wait_interval_->pending_configure_job == to_be_canary.get()); |
1046 SDVLOG(2) << "Unthrottled " << (to_be_canary.get() ? "with " : "without ") | 1083 SDVLOG(2) << "Unthrottled " << (to_be_canary.get() ? "with " : "without ") |
1047 << "canary."; | 1084 << "canary."; |
1048 | 1085 |
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1151 | 1188 |
1152 #undef SDVLOG_LOC | 1189 #undef SDVLOG_LOC |
1153 | 1190 |
1154 #undef SDVLOG | 1191 #undef SDVLOG |
1155 | 1192 |
1156 #undef SLOG | 1193 #undef SLOG |
1157 | 1194 |
1158 #undef ENUM_CASE | 1195 #undef ENUM_CASE |
1159 | 1196 |
1160 } // namespace syncer | 1197 } // namespace syncer |
OLD | NEW |