• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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