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.
5 #include "chrome/browser/sync_file_system/sync_process_runner.h"
7 #include "base/format_macros.h"
8 #include "chrome/browser/sync_file_system/logger.h"
10 namespace sync_file_system
{
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
25 class BaseTimerHelper
: public SyncProcessRunner::TimerHelper
{
29 virtual bool IsRunning() override
{
30 return timer_
.IsRunning();
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
);
39 virtual base::TimeTicks
Now() const override
{
40 return base::TimeTicks::Now();
43 virtual ~BaseTimerHelper() {}
46 base::OneShotTimer
<SyncProcessRunner
> timer_
;
48 DISALLOW_COPY_AND_ASSIGN(BaseTimerHelper
);
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
;
62 SyncProcessRunner::SyncProcessRunner(
63 const std::string
& name
,
65 scoped_ptr
<TimerHelper
> timer_helper
,
66 size_t max_parallel_task
)
69 max_parallel_task_(max_parallel_task
),
71 timer_helper_(timer_helper
.Pass()),
72 service_state_(SYNC_SERVICE_RUNNING
),
75 DCHECK_LE(1u, max_parallel_task_
);
77 timer_helper_
.reset(new BaseTimerHelper
);
80 SyncProcessRunner::~SyncProcessRunner() {}
82 void SyncProcessRunner::Schedule() {
83 if (pending_changes_
== 0) {
84 ScheduleInternal(kSyncDelayMaxInMilliseconds
);
88 SyncServiceState last_service_state
= service_state_
;
89 service_state_
= GetServiceState();
91 switch (service_state_
) {
92 case SYNC_SERVICE_RUNNING
:
94 if (pending_changes_
> kPendingChangeThresholdForFastSync
)
95 ScheduleInternal(kSyncDelayFastInMilliseconds
);
97 ScheduleInternal(kSyncDelayInMilliseconds
);
100 case SYNC_SERVICE_TEMPORARY_UNAVAILABLE
:
101 if (last_service_state
!= service_state_
)
102 ThrottleSync(kSyncDelaySlowInMilliseconds
);
103 ScheduleInternal(kSyncDelaySlowInMilliseconds
);
106 case SYNC_SERVICE_AUTHENTICATION_REQUIRED
:
107 case SYNC_SERVICE_DISABLED
:
108 if (last_service_state
!= service_state_
)
109 ThrottleSync(kSyncDelaySlowInMilliseconds
);
110 ScheduleInternal(kSyncDelayMaxInMilliseconds
);
115 ScheduleInternal(kSyncDelayMaxInMilliseconds
);
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
);
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
129 base::TimeDelta base_delay_delta
=
130 base::TimeDelta::FromMilliseconds(base_delay
);
131 const base::TimeDelta max_delay
=
132 base::TimeDelta::FromMilliseconds(kSyncDelayMaxInMilliseconds
);
134 std::min(now
+ max_delay
,
135 std::max(now
+ base_delay_delta
, throttle_until_
+ 2 * elapsed
));
138 void SyncProcessRunner::ResetOldThrottling() {
139 if (throttle_until_
< base::TimeTicks::Now())
143 void SyncProcessRunner::ResetThrottling() {
144 throttle_from_
= base::TimeTicks();
145 throttle_until_
= base::TimeTicks();
148 SyncServiceState
SyncProcessRunner::GetServiceState() {
149 return client_
->GetSyncServiceState();
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
) {
159 util::Log(logging::LOG_VERBOSE
, FROM_HERE
,
160 "[%s] pending_changes updated: %" PRId64
,
161 name_
.c_str(), pending_changes
);
166 SyncFileSystemService
* SyncProcessRunner::GetSyncService() {
167 return client_
->GetSyncService();
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_
);
176 util::Log(logging::LOG_VERBOSE
, FROM_HERE
,
177 "[%s] * Finished (elapsed: %" PRId64
" ms)", name_
.c_str(),
178 (timer_helper_
->Now() - start_time
).InMilliseconds());
180 if (status
== SYNC_STATUS_NO_CHANGE_TO_SYNC
||
181 status
== SYNC_STATUS_FILE_BUSY
) {
182 ScheduleInternal(kSyncDelayMaxInMilliseconds
);
186 if (WasSuccessfulSync(status
))
187 ResetOldThrottling();
189 ThrottleSync(kSyncDelayWithSyncError
);
194 void SyncProcessRunner::Run() {
195 if (running_tasks_
>= max_parallel_task_
)
198 base::TimeTicks now
= timer_helper_
->Now();
201 util::Log(logging::LOG_VERBOSE
, FROM_HERE
,
202 "[%s] * Started", name_
.c_str());
204 StartSync(base::Bind(&SyncProcessRunner::Finished
, factory_
.GetWeakPtr(),
206 if (running_tasks_
< max_parallel_task_
)
210 void SyncProcessRunner::ScheduleInternal(int64 delay
) {
211 base::TimeTicks now
= timer_helper_
->Now();
212 base::TimeTicks next_scheduled
;
214 if (timer_helper_
->IsRunning()) {
215 next_scheduled
= last_run_
+ base::TimeDelta::FromMilliseconds(delay
);
216 if (next_scheduled
< now
) {
218 now
+ base::TimeDelta::FromMilliseconds(kSyncDelayFastInMilliseconds
);
221 next_scheduled
= now
+ base::TimeDelta::FromMilliseconds(delay
);
224 if (next_scheduled
< throttle_until_
)
225 next_scheduled
= throttle_until_
;
227 if (timer_helper_
->IsRunning() && last_scheduled_
== next_scheduled
)
230 util::Log(logging::LOG_VERBOSE
, FROM_HERE
,
231 "[%s] Scheduling task in %" PRId64
" ms",
232 name_
.c_str(), (next_scheduled
- now
).InMilliseconds());
234 last_scheduled_
= next_scheduled
;
236 timer_helper_
->Start(
237 FROM_HERE
, next_scheduled
- now
,
238 base::Bind(&SyncProcessRunner::Run
, base::Unretained(this)));
241 void SyncProcessRunner::CheckIfIdle() {
242 if (pending_changes_
== 0 && running_tasks_
== 0)
243 client_
->OnSyncIdle();
246 } // namespace sync_file_system