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