Home | History | Annotate | Download | only in sync_file_system
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/sync_file_system/sync_process_runner.h"
      6 
      7 #include "base/format_macros.h"
      8 #include "chrome/browser/sync_file_system/logger.h"
      9 
     10 namespace sync_file_system {
     11 
     12 const int64 SyncProcessRunner::kSyncDelayInMilliseconds =
     13     1 * base::Time::kMillisecondsPerSecond; // 1 sec
     14 const int64 SyncProcessRunner::kSyncDelayWithSyncError =
     15     3 * base::Time::kMillisecondsPerSecond; // 3 sec
     16 const int64 SyncProcessRunner::kSyncDelayFastInMilliseconds = 100;  // 100 ms
     17 const int SyncProcessRunner::kPendingChangeThresholdForFastSync = 10;
     18 const int64 SyncProcessRunner::kSyncDelaySlowInMilliseconds =
     19     30 * base::Time::kMillisecondsPerSecond;  // 30 sec
     20 const int64 SyncProcessRunner::kSyncDelayMaxInMilliseconds =
     21     30 * 60 * base::Time::kMillisecondsPerSecond;  // 30 min
     22 
     23 namespace {
     24 
     25 class BaseTimerHelper : public SyncProcessRunner::TimerHelper {
     26  public:
     27   BaseTimerHelper() {}
     28 
     29   virtual bool IsRunning() OVERRIDE {
     30     return timer_.IsRunning();
     31   }
     32 
     33   virtual void Start(const tracked_objects::Location& from_here,
     34                      const base::TimeDelta& delay,
     35                      const base::Closure& closure) OVERRIDE {
     36     timer_.Start(from_here, delay, closure);
     37   }
     38 
     39   virtual base::TimeTicks Now() const OVERRIDE {
     40     return base::TimeTicks::Now();
     41   }
     42 
     43   virtual ~BaseTimerHelper() {}
     44 
     45  private:
     46   base::OneShotTimer<SyncProcessRunner> timer_;
     47 
     48   DISALLOW_COPY_AND_ASSIGN(BaseTimerHelper);
     49 };
     50 
     51 bool WasSuccessfulSync(SyncStatusCode status) {
     52   return status == SYNC_STATUS_OK ||
     53          status == SYNC_STATUS_HAS_CONFLICT ||
     54          status == SYNC_STATUS_NO_CONFLICT ||
     55          status == SYNC_STATUS_NO_CHANGE_TO_SYNC ||
     56          status == SYNC_STATUS_UNKNOWN_ORIGIN ||
     57          status == SYNC_STATUS_RETRY;
     58 }
     59 
     60 }  // namespace
     61 
     62 SyncProcessRunner::SyncProcessRunner(
     63     const std::string& name,
     64     Client* client,
     65     scoped_ptr<TimerHelper> timer_helper,
     66     size_t max_parallel_task)
     67     : name_(name),
     68       client_(client),
     69       max_parallel_task_(max_parallel_task),
     70       running_tasks_(0),
     71       timer_helper_(timer_helper.Pass()),
     72       service_state_(SYNC_SERVICE_RUNNING),
     73       pending_changes_(0),
     74       factory_(this) {
     75   DCHECK_LE(1u, max_parallel_task_);
     76   if (!timer_helper_)
     77     timer_helper_.reset(new BaseTimerHelper);
     78 }
     79 
     80 SyncProcessRunner::~SyncProcessRunner() {}
     81 
     82 void SyncProcessRunner::Schedule() {
     83   if (pending_changes_ == 0) {
     84     ScheduleInternal(kSyncDelayMaxInMilliseconds);
     85     return;
     86   }
     87 
     88   SyncServiceState last_service_state = service_state_;
     89   service_state_ = GetServiceState();
     90 
     91   switch (service_state_) {
     92     case SYNC_SERVICE_RUNNING:
     93       ResetThrottling();
     94       if (pending_changes_ > kPendingChangeThresholdForFastSync)
     95         ScheduleInternal(kSyncDelayFastInMilliseconds);
     96       else
     97         ScheduleInternal(kSyncDelayInMilliseconds);
     98       return;
     99 
    100     case SYNC_SERVICE_TEMPORARY_UNAVAILABLE:
    101       if (last_service_state != service_state_)
    102         ThrottleSync(kSyncDelaySlowInMilliseconds);
    103       ScheduleInternal(kSyncDelaySlowInMilliseconds);
    104       return;
    105 
    106     case SYNC_SERVICE_AUTHENTICATION_REQUIRED:
    107     case SYNC_SERVICE_DISABLED:
    108       if (last_service_state != service_state_)
    109         ThrottleSync(kSyncDelaySlowInMilliseconds);
    110       ScheduleInternal(kSyncDelayMaxInMilliseconds);
    111       return;
    112   }
    113 
    114   NOTREACHED();
    115   ScheduleInternal(kSyncDelayMaxInMilliseconds);
    116 }
    117 
    118 void SyncProcessRunner::ThrottleSync(int64 base_delay) {
    119   base::TimeTicks now = timer_helper_->Now();
    120   base::TimeDelta elapsed = std::min(now, throttle_until_) - throttle_from_;
    121   DCHECK(base::TimeDelta() <= elapsed);
    122 
    123   throttle_from_ = now;
    124   // Extend throttling duration by twice the elapsed time.
    125   // That is, if the backoff repeats in a short period, the throttling period
    126   // doesn't grow exponentially.  If the backoff happens on the end of
    127   // throttling period, it causes another throttling period that is twice as
    128   // long as previous.
    129   base::TimeDelta base_delay_delta =
    130       base::TimeDelta::FromMilliseconds(base_delay);
    131   const base::TimeDelta max_delay =
    132       base::TimeDelta::FromMilliseconds(kSyncDelayMaxInMilliseconds);
    133   throttle_until_ =
    134       std::min(now + max_delay,
    135                std::max(now + base_delay_delta, throttle_until_ + 2 * elapsed));
    136 }
    137 
    138 void SyncProcessRunner::ResetOldThrottling() {
    139   if (throttle_until_ < base::TimeTicks::Now())
    140     ResetThrottling();
    141 }
    142 
    143 void SyncProcessRunner::ResetThrottling() {
    144   throttle_from_ = base::TimeTicks();
    145   throttle_until_ = base::TimeTicks();
    146 }
    147 
    148 SyncServiceState SyncProcessRunner::GetServiceState() {
    149   return client_->GetSyncServiceState();
    150 }
    151 
    152 void SyncProcessRunner::OnChangesUpdated(
    153     int64 pending_changes) {
    154   DCHECK_GE(pending_changes, 0);
    155   int64 old_pending_changes = pending_changes_;
    156   pending_changes_ = pending_changes;
    157   if (old_pending_changes != pending_changes) {
    158     CheckIfIdle();
    159     util::Log(logging::LOG_VERBOSE, FROM_HERE,
    160               "[%s] pending_changes updated: %" PRId64,
    161               name_.c_str(), pending_changes);
    162   }
    163   Schedule();
    164 }
    165 
    166 SyncFileSystemService* SyncProcessRunner::GetSyncService() {
    167   return client_->GetSyncService();
    168 }
    169 
    170 void SyncProcessRunner::Finished(const base::TimeTicks& start_time,
    171                                  SyncStatusCode status) {
    172   DCHECK_LT(0u, running_tasks_);
    173   DCHECK_LE(running_tasks_, max_parallel_task_);
    174   --running_tasks_;
    175   CheckIfIdle();
    176   util::Log(logging::LOG_VERBOSE, FROM_HERE,
    177             "[%s] * Finished (elapsed: %" PRId64 " ms)", name_.c_str(),
    178             (timer_helper_->Now() - start_time).InMilliseconds());
    179 
    180   if (status == SYNC_STATUS_NO_CHANGE_TO_SYNC ||
    181       status == SYNC_STATUS_FILE_BUSY) {
    182     ScheduleInternal(kSyncDelayMaxInMilliseconds);
    183     return;
    184   }
    185 
    186   if (WasSuccessfulSync(status))
    187     ResetOldThrottling();
    188   else
    189     ThrottleSync(kSyncDelayWithSyncError);
    190 
    191   Schedule();
    192 }
    193 
    194 void SyncProcessRunner::Run() {
    195   if (running_tasks_ >= max_parallel_task_)
    196     return;
    197   ++running_tasks_;
    198   base::TimeTicks now = timer_helper_->Now();
    199   last_run_ = now;
    200 
    201   util::Log(logging::LOG_VERBOSE, FROM_HERE,
    202             "[%s] * Started", name_.c_str());
    203 
    204   StartSync(base::Bind(&SyncProcessRunner::Finished, factory_.GetWeakPtr(),
    205                        now));
    206   if (running_tasks_ < max_parallel_task_)
    207     Schedule();
    208 }
    209 
    210 void SyncProcessRunner::ScheduleInternal(int64 delay) {
    211   base::TimeTicks now = timer_helper_->Now();
    212   base::TimeTicks next_scheduled;
    213 
    214   if (timer_helper_->IsRunning()) {
    215     next_scheduled = last_run_ + base::TimeDelta::FromMilliseconds(delay);
    216     if (next_scheduled < now) {
    217       next_scheduled =
    218           now + base::TimeDelta::FromMilliseconds(kSyncDelayFastInMilliseconds);
    219     }
    220   } else {
    221     next_scheduled = now + base::TimeDelta::FromMilliseconds(delay);
    222   }
    223 
    224   if (next_scheduled < throttle_until_)
    225     next_scheduled = throttle_until_;
    226 
    227   if (timer_helper_->IsRunning() && last_scheduled_ == next_scheduled)
    228     return;
    229 
    230   util::Log(logging::LOG_VERBOSE, FROM_HERE,
    231             "[%s] Scheduling task in %" PRId64 " ms",
    232             name_.c_str(), (next_scheduled - now).InMilliseconds());
    233 
    234   last_scheduled_ = next_scheduled;
    235 
    236   timer_helper_->Start(
    237       FROM_HERE, next_scheduled - now,
    238       base::Bind(&SyncProcessRunner::Run, base::Unretained(this)));
    239 }
    240 
    241 void SyncProcessRunner::CheckIfIdle() {
    242   if (pending_changes_ == 0 && running_tasks_ == 0)
    243     client_->OnSyncIdle();
    244 }
    245 
    246 }  // namespace sync_file_system
    247