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:
BaseTimerHelper()27 BaseTimerHelper() {}
28
IsRunning()29 virtual bool IsRunning() OVERRIDE {
30 return timer_.IsRunning();
31 }
32
Start(const tracked_objects::Location & from_here,const base::TimeDelta & delay,const base::Closure & closure)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
Now() const39 virtual base::TimeTicks Now() const OVERRIDE {
40 return base::TimeTicks::Now();
41 }
42
~BaseTimerHelper()43 virtual ~BaseTimerHelper() {}
44
45 private:
46 base::OneShotTimer<SyncProcessRunner> timer_;
47
48 DISALLOW_COPY_AND_ASSIGN(BaseTimerHelper);
49 };
50
WasSuccessfulSync(SyncStatusCode status)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
SyncProcessRunner(const std::string & name,Client * client,scoped_ptr<TimerHelper> timer_helper,size_t max_parallel_task)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
~SyncProcessRunner()80 SyncProcessRunner::~SyncProcessRunner() {}
81
Schedule()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
ThrottleSync(int64 base_delay)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
ResetOldThrottling()138 void SyncProcessRunner::ResetOldThrottling() {
139 if (throttle_until_ < base::TimeTicks::Now())
140 ResetThrottling();
141 }
142
ResetThrottling()143 void SyncProcessRunner::ResetThrottling() {
144 throttle_from_ = base::TimeTicks();
145 throttle_until_ = base::TimeTicks();
146 }
147
GetServiceState()148 SyncServiceState SyncProcessRunner::GetServiceState() {
149 return client_->GetSyncServiceState();
150 }
151
OnChangesUpdated(int64 pending_changes)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
GetSyncService()166 SyncFileSystemService* SyncProcessRunner::GetSyncService() {
167 return client_->GetSyncService();
168 }
169
Finished(const base::TimeTicks & start_time,SyncStatusCode status)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
Run()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
ScheduleInternal(int64 delay)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
CheckIfIdle()241 void SyncProcessRunner::CheckIfIdle() {
242 if (pending_changes_ == 0 && running_tasks_ == 0)
243 client_->OnSyncIdle();
244 }
245
246 } // namespace sync_file_system
247