sync_process_runner.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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#include "chrome/browser/sync_file_system/sync_file_system_service.h"
10
11namespace sync_file_system {
12
13namespace {
14
15// Default delay when more changes are available.
16const int64 kSyncDelayInMilliseconds = 1 * base::Time::kMillisecondsPerSecond;
17
18// Default delay when the previous change has had an error (but remote service
19// is running).
20const int64 kSyncDelayWithSyncError = 3 * base::Time::kMillisecondsPerSecond;
21
22// Default delay when there're more than 10 pending changes.
23const int64 kSyncDelayFastInMilliseconds = 100;
24const int kPendingChangeThresholdForFastSync = 10;
25
26// Default delay when remote service is temporarily unavailable.
27const int64 kSyncDelaySlowInMilliseconds =
28    30 * base::Time::kMillisecondsPerSecond;  // Start with 30 sec + exp backoff
29
30// Default delay when there're no changes.
31const int64 kSyncDelayMaxInMilliseconds =
32    30 * 60 * base::Time::kMillisecondsPerSecond;  // 30 min
33
34bool WasSuccessfulSync(SyncStatusCode status) {
35  return status == SYNC_STATUS_OK ||
36         status == SYNC_STATUS_HAS_CONFLICT ||
37         status == SYNC_STATUS_NO_CONFLICT ||
38         status == SYNC_STATUS_NO_CHANGE_TO_SYNC ||
39         status == SYNC_STATUS_UNKNOWN_ORIGIN ||
40         status == SYNC_STATUS_RETRY;
41}
42
43}  // namespace
44
45SyncProcessRunner::SyncProcessRunner(
46    const std::string& name,
47    SyncFileSystemService* sync_service)
48    : name_(name),
49      sync_service_(sync_service),
50      current_delay_(0),
51      last_delay_(0),
52      pending_changes_(0),
53      running_(false),
54      factory_(this) {}
55
56SyncProcessRunner::~SyncProcessRunner() {}
57
58void SyncProcessRunner::Schedule() {
59  int64 delay = kSyncDelayInMilliseconds;
60  if (pending_changes_ == 0) {
61    ScheduleInternal(kSyncDelayMaxInMilliseconds);
62    return;
63  }
64  switch (GetServiceState()) {
65    case SYNC_SERVICE_RUNNING:
66      if (pending_changes_ > kPendingChangeThresholdForFastSync)
67        delay = kSyncDelayFastInMilliseconds;
68      else
69        delay = kSyncDelayInMilliseconds;
70      break;
71
72    case SYNC_SERVICE_TEMPORARY_UNAVAILABLE:
73      delay = kSyncDelaySlowInMilliseconds;
74      if (last_delay_ >= kSyncDelaySlowInMilliseconds)
75        delay = last_delay_ * 2;
76      if (delay >= kSyncDelayMaxInMilliseconds)
77        delay = kSyncDelayMaxInMilliseconds;
78      break;
79
80    case SYNC_SERVICE_AUTHENTICATION_REQUIRED:
81    case SYNC_SERVICE_DISABLED:
82      delay = kSyncDelayMaxInMilliseconds;
83      break;
84  }
85  ScheduleInternal(delay);
86}
87
88void SyncProcessRunner::ScheduleIfNotRunning() {
89  if (!timer_.IsRunning())
90    Schedule();
91}
92
93void SyncProcessRunner::OnChangesUpdated(
94    int64 pending_changes) {
95  DCHECK_GE(pending_changes, 0);
96  int64 old_pending_changes = pending_changes_;
97  pending_changes_ = pending_changes;
98  if (old_pending_changes != pending_changes) {
99    if (pending_changes == 0)
100      sync_service()->OnSyncIdle();
101    util::Log(logging::LOG_VERBOSE, FROM_HERE,
102              "[%s] pending_changes updated: %" PRId64,
103              name_.c_str(), pending_changes);
104  }
105  Schedule();
106}
107
108SyncServiceState SyncProcessRunner::GetServiceState() {
109  return sync_service()->GetSyncServiceState();
110}
111
112void SyncProcessRunner::Finished(SyncStatusCode status) {
113  DCHECK(running_);
114  running_ = false;
115  util::Log(logging::LOG_VERBOSE, FROM_HERE,
116            "[%s] * Finished (elapsed: %" PRId64 " sec)",
117            name_.c_str(),
118            (base::Time::Now() - last_scheduled_).InSeconds());
119  if (status == SYNC_STATUS_NO_CHANGE_TO_SYNC ||
120      status == SYNC_STATUS_FILE_BUSY)
121    ScheduleInternal(kSyncDelayMaxInMilliseconds);
122  else if (!WasSuccessfulSync(status) &&
123           GetServiceState() == SYNC_SERVICE_RUNNING)
124    ScheduleInternal(kSyncDelayWithSyncError);
125  else
126    Schedule();
127}
128
129void SyncProcessRunner::Run() {
130  if (running_)
131    return;
132  running_ = true;
133  last_scheduled_ = base::Time::Now();
134  last_delay_ = current_delay_;
135
136  util::Log(logging::LOG_VERBOSE, FROM_HERE,
137            "[%s] * Started", name_.c_str());
138
139  StartSync(
140      base::Bind(&SyncProcessRunner::Finished, factory_.GetWeakPtr()));
141}
142
143void SyncProcessRunner::ScheduleInternal(int64 delay) {
144  base::TimeDelta time_to_next = base::TimeDelta::FromMilliseconds(delay);
145
146  if (timer_.IsRunning()) {
147    if (current_delay_ == delay)
148      return;
149
150    base::TimeDelta elapsed = base::Time::Now() - last_scheduled_;
151    if (elapsed < time_to_next) {
152      time_to_next = time_to_next - elapsed;
153    } else {
154      time_to_next = base::TimeDelta::FromMilliseconds(
155          kSyncDelayFastInMilliseconds);
156    }
157    timer_.Stop();
158  }
159
160  if (current_delay_ != delay) {
161    util::Log(logging::LOG_VERBOSE, FROM_HERE,
162              "[%s] Scheduling task in %" PRId64 " secs",
163              name_.c_str(), time_to_next.InSeconds());
164  }
165  current_delay_ = delay;
166
167  timer_.Start(FROM_HERE, time_to_next, this, &SyncProcessRunner::Run);
168}
169
170}  // namespace sync_file_system
171