Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / sync_file_system / sync_process_runner.cc
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