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 bool IsRunning() override
{ return timer_
.IsRunning(); }
31 void Start(const tracked_objects::Location
& from_here
,
32 const base::TimeDelta
& delay
,
33 const base::Closure
& closure
) override
{
34 timer_
.Start(from_here
, delay
, closure
);
37 base::TimeTicks
Now() const override
{ return base::TimeTicks::Now(); }
39 ~BaseTimerHelper() override
{}
42 base::OneShotTimer
<SyncProcessRunner
> timer_
;
44 DISALLOW_COPY_AND_ASSIGN(BaseTimerHelper
);
47 bool WasSuccessfulSync(SyncStatusCode status
) {
48 return status
== SYNC_STATUS_OK
||
49 status
== SYNC_STATUS_HAS_CONFLICT
||
50 status
== SYNC_STATUS_NO_CONFLICT
||
51 status
== SYNC_STATUS_NO_CHANGE_TO_SYNC
||
52 status
== SYNC_STATUS_UNKNOWN_ORIGIN
||
53 status
== SYNC_STATUS_RETRY
;
58 SyncProcessRunner::SyncProcessRunner(
59 const std::string
& name
,
61 scoped_ptr
<TimerHelper
> timer_helper
,
62 size_t max_parallel_task
)
65 max_parallel_task_(max_parallel_task
),
67 timer_helper_(timer_helper
.Pass()),
68 service_state_(SYNC_SERVICE_RUNNING
),
71 DCHECK_LE(1u, max_parallel_task_
);
73 timer_helper_
.reset(new BaseTimerHelper
);
76 SyncProcessRunner::~SyncProcessRunner() {}
78 void SyncProcessRunner::Schedule() {
79 if (pending_changes_
== 0) {
80 ScheduleInternal(kSyncDelayMaxInMilliseconds
);
84 SyncServiceState last_service_state
= service_state_
;
85 service_state_
= GetServiceState();
87 switch (service_state_
) {
88 case SYNC_SERVICE_RUNNING
:
90 if (pending_changes_
> kPendingChangeThresholdForFastSync
)
91 ScheduleInternal(kSyncDelayFastInMilliseconds
);
93 ScheduleInternal(kSyncDelayInMilliseconds
);
96 case SYNC_SERVICE_TEMPORARY_UNAVAILABLE
:
97 if (last_service_state
!= service_state_
)
98 ThrottleSync(kSyncDelaySlowInMilliseconds
);
99 ScheduleInternal(kSyncDelaySlowInMilliseconds
);
102 case SYNC_SERVICE_AUTHENTICATION_REQUIRED
:
103 case SYNC_SERVICE_DISABLED
:
104 if (last_service_state
!= service_state_
)
105 ThrottleSync(kSyncDelaySlowInMilliseconds
);
106 ScheduleInternal(kSyncDelayMaxInMilliseconds
);
111 ScheduleInternal(kSyncDelayMaxInMilliseconds
);
114 void SyncProcessRunner::ThrottleSync(int64 base_delay
) {
115 base::TimeTicks now
= timer_helper_
->Now();
116 base::TimeDelta elapsed
= std::min(now
, throttle_until_
) - throttle_from_
;
117 DCHECK(base::TimeDelta() <= elapsed
);
119 throttle_from_
= now
;
120 // Extend throttling duration by twice the elapsed time.
121 // That is, if the backoff repeats in a short period, the throttling period
122 // doesn't grow exponentially. If the backoff happens on the end of
123 // throttling period, it causes another throttling period that is twice as
125 base::TimeDelta base_delay_delta
=
126 base::TimeDelta::FromMilliseconds(base_delay
);
127 const base::TimeDelta max_delay
=
128 base::TimeDelta::FromMilliseconds(kSyncDelayMaxInMilliseconds
);
130 std::min(now
+ max_delay
,
131 std::max(now
+ base_delay_delta
, throttle_until_
+ 2 * elapsed
));
134 void SyncProcessRunner::ResetOldThrottling() {
135 if (throttle_until_
< base::TimeTicks::Now())
139 void SyncProcessRunner::ResetThrottling() {
140 throttle_from_
= base::TimeTicks();
141 throttle_until_
= base::TimeTicks();
144 SyncServiceState
SyncProcessRunner::GetServiceState() {
145 return client_
->GetSyncServiceState();
148 void SyncProcessRunner::OnChangesUpdated(
149 int64 pending_changes
) {
150 DCHECK_GE(pending_changes
, 0);
151 int64 old_pending_changes
= pending_changes_
;
152 pending_changes_
= pending_changes
;
153 if (old_pending_changes
!= pending_changes
) {
155 util::Log(logging::LOG_VERBOSE
, FROM_HERE
,
156 "[%s] pending_changes updated: %" PRId64
,
157 name_
.c_str(), pending_changes
);
162 SyncFileSystemService
* SyncProcessRunner::GetSyncService() {
163 return client_
->GetSyncService();
166 void SyncProcessRunner::Finished(const base::TimeTicks
& start_time
,
167 SyncStatusCode status
) {
168 DCHECK_LT(0u, running_tasks_
);
169 DCHECK_LE(running_tasks_
, max_parallel_task_
);
172 util::Log(logging::LOG_VERBOSE
, FROM_HERE
,
173 "[%s] * Finished (elapsed: %" PRId64
" ms)", name_
.c_str(),
174 (timer_helper_
->Now() - start_time
).InMilliseconds());
176 if (status
== SYNC_STATUS_NO_CHANGE_TO_SYNC
||
177 status
== SYNC_STATUS_FILE_BUSY
) {
178 ScheduleInternal(kSyncDelayMaxInMilliseconds
);
182 if (WasSuccessfulSync(status
))
183 ResetOldThrottling();
185 ThrottleSync(kSyncDelayWithSyncError
);
190 void SyncProcessRunner::Run() {
191 if (running_tasks_
>= max_parallel_task_
)
194 base::TimeTicks now
= timer_helper_
->Now();
197 util::Log(logging::LOG_VERBOSE
, FROM_HERE
,
198 "[%s] * Started", name_
.c_str());
200 StartSync(base::Bind(&SyncProcessRunner::Finished
, factory_
.GetWeakPtr(),
202 if (running_tasks_
< max_parallel_task_
)
206 void SyncProcessRunner::ScheduleInternal(int64 delay
) {
207 base::TimeTicks now
= timer_helper_
->Now();
208 base::TimeTicks next_scheduled
;
210 if (timer_helper_
->IsRunning()) {
211 next_scheduled
= last_run_
+ base::TimeDelta::FromMilliseconds(delay
);
212 if (next_scheduled
< now
) {
214 now
+ base::TimeDelta::FromMilliseconds(kSyncDelayFastInMilliseconds
);
217 next_scheduled
= now
+ base::TimeDelta::FromMilliseconds(delay
);
220 if (next_scheduled
< throttle_until_
)
221 next_scheduled
= throttle_until_
;
223 if (timer_helper_
->IsRunning() && last_scheduled_
== next_scheduled
)
226 util::Log(logging::LOG_VERBOSE
, FROM_HERE
,
227 "[%s] Scheduling task in %" PRId64
" ms",
228 name_
.c_str(), (next_scheduled
- now
).InMilliseconds());
230 last_scheduled_
= next_scheduled
;
232 timer_helper_
->Start(
233 FROM_HERE
, next_scheduled
- now
,
234 base::Bind(&SyncProcessRunner::Run
, base::Unretained(this)));
237 void SyncProcessRunner::CheckIfIdle() {
238 if (pending_changes_
== 0 && running_tasks_
== 0)
239 client_
->OnSyncIdle();
242 } // namespace sync_file_system