sync_process_runner.cc revision 03b57e008b61dfcb1fbad3aea950ae0e001748b0
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
10namespace sync_file_system {
11
12const int64 SyncProcessRunner::kSyncDelayInMilliseconds =
13    1 * base::Time::kMillisecondsPerSecond; // 1 sec
14const int64 SyncProcessRunner::kSyncDelayWithSyncError =
15    3 * base::Time::kMillisecondsPerSecond; // 3 sec
16const int64 SyncProcessRunner::kSyncDelayFastInMilliseconds = 100;  // 100 ms
17const int SyncProcessRunner::kPendingChangeThresholdForFastSync = 10;
18const int64 SyncProcessRunner::kSyncDelaySlowInMilliseconds =
19    30 * base::Time::kMillisecondsPerSecond;  // 30 sec
20const int64 SyncProcessRunner::kSyncDelayMaxInMilliseconds =
21    30 * 60 * base::Time::kMillisecondsPerSecond;  // 30 min
22
23namespace {
24
25class 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
51bool 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
62SyncProcessRunner::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
80SyncProcessRunner::~SyncProcessRunner() {}
81
82void 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
118void 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
138void SyncProcessRunner::ResetOldThrottling() {
139  if (throttle_until_ < base::TimeTicks::Now())
140    ResetThrottling();
141}
142
143void SyncProcessRunner::ResetThrottling() {
144  throttle_from_ = base::TimeTicks();
145  throttle_until_ = base::TimeTicks();
146}
147
148SyncServiceState SyncProcessRunner::GetServiceState() {
149  return client_->GetSyncServiceState();
150}
151
152void 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
166SyncFileSystemService* SyncProcessRunner::GetSyncService() {
167  return client_->GetSyncService();
168}
169
170void 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
194void 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
210void 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
241void SyncProcessRunner::CheckIfIdle() {
242  if (pending_changes_ == 0 && running_tasks_ == 0)
243    client_->OnSyncIdle();
244}
245
246}  // namespace sync_file_system
247