blob: 2696585ae5329e12fc141a203316ee2b9440148b [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/sync_file_system/sync_process_runner.h"
#include "base/format_macros.h"
#include "chrome/browser/sync_file_system/logger.h"
#include "chrome/browser/sync_file_system/sync_file_system_service.h"
namespace sync_file_system {
namespace {
// Default delay when more changes are available.
const int64 kSyncDelayInMilliseconds = 1 * base::Time::kMillisecondsPerSecond;
// Default delay when the previous change has had an error (but remote service
// is running).
const int64 kSyncDelayWithSyncError = 3 * base::Time::kMillisecondsPerSecond;
// Default delay when there're more than 10 pending changes.
const int64 kSyncDelayFastInMilliseconds = 100;
const int kPendingChangeThresholdForFastSync = 10;
// Default delay when remote service is temporarily unavailable.
const int64 kSyncDelaySlowInMilliseconds =
30 * base::Time::kMillisecondsPerSecond; // Start with 30 sec + exp backoff
// Default delay when there're no changes.
const int64 kSyncDelayMaxInMilliseconds =
30 * 60 * base::Time::kMillisecondsPerSecond; // 30 min
bool WasSuccessfulSync(SyncStatusCode status) {
return status == SYNC_STATUS_OK ||
status == SYNC_STATUS_HAS_CONFLICT ||
status == SYNC_STATUS_NO_CONFLICT ||
status == SYNC_STATUS_NO_CHANGE_TO_SYNC ||
status == SYNC_STATUS_UNKNOWN_ORIGIN ||
status == SYNC_STATUS_RETRY;
}
} // namespace
SyncProcessRunner::SyncProcessRunner(
const std::string& name,
SyncFileSystemService* sync_service)
: name_(name),
sync_service_(sync_service),
current_delay_(0),
last_delay_(0),
pending_changes_(0),
running_(false),
factory_(this) {}
SyncProcessRunner::~SyncProcessRunner() {}
void SyncProcessRunner::Schedule() {
int64 delay = kSyncDelayInMilliseconds;
if (pending_changes_ == 0) {
ScheduleInternal(kSyncDelayMaxInMilliseconds);
return;
}
switch (GetServiceState()) {
case SYNC_SERVICE_RUNNING:
if (pending_changes_ > kPendingChangeThresholdForFastSync)
delay = kSyncDelayFastInMilliseconds;
else
delay = kSyncDelayInMilliseconds;
break;
case SYNC_SERVICE_TEMPORARY_UNAVAILABLE:
delay = kSyncDelaySlowInMilliseconds;
if (last_delay_ >= kSyncDelaySlowInMilliseconds)
delay = last_delay_ * 2;
if (delay >= kSyncDelayMaxInMilliseconds)
delay = kSyncDelayMaxInMilliseconds;
break;
case SYNC_SERVICE_AUTHENTICATION_REQUIRED:
case SYNC_SERVICE_DISABLED:
delay = kSyncDelayMaxInMilliseconds;
break;
}
ScheduleInternal(delay);
}
void SyncProcessRunner::ScheduleIfNotRunning() {
if (!timer_.IsRunning())
Schedule();
}
void SyncProcessRunner::OnChangesUpdated(
int64 pending_changes) {
DCHECK_GE(pending_changes, 0);
if (pending_changes_ != pending_changes) {
util::Log(logging::LOG_VERBOSE, FROM_HERE,
"[%s] pending_changes updated: %" PRId64,
name_.c_str(), pending_changes);
}
pending_changes_ = pending_changes;
Schedule();
}
SyncServiceState SyncProcessRunner::GetServiceState() {
return sync_service()->GetSyncServiceState();
}
void SyncProcessRunner::Finished(SyncStatusCode status) {
DCHECK(running_);
running_ = false;
util::Log(logging::LOG_VERBOSE, FROM_HERE,
"[%s] * Finished (elapsed: %" PRId64 " sec)",
name_.c_str(),
(base::Time::Now() - last_scheduled_).InSeconds());
if (status == SYNC_STATUS_NO_CHANGE_TO_SYNC ||
status == SYNC_STATUS_FILE_BUSY)
ScheduleInternal(kSyncDelayMaxInMilliseconds);
else if (!WasSuccessfulSync(status) &&
GetServiceState() == SYNC_SERVICE_RUNNING)
ScheduleInternal(kSyncDelayWithSyncError);
else
Schedule();
}
void SyncProcessRunner::Run() {
if (running_)
return;
running_ = true;
last_scheduled_ = base::Time::Now();
last_delay_ = current_delay_;
util::Log(logging::LOG_VERBOSE, FROM_HERE,
"[%s] * Started", name_.c_str());
StartSync(
base::Bind(&SyncProcessRunner::Finished, factory_.GetWeakPtr()));
}
void SyncProcessRunner::ScheduleInternal(int64 delay) {
base::TimeDelta time_to_next = base::TimeDelta::FromMilliseconds(delay);
if (timer_.IsRunning()) {
if (current_delay_ == delay)
return;
base::TimeDelta elapsed = base::Time::Now() - last_scheduled_;
if (elapsed < time_to_next) {
time_to_next = time_to_next - elapsed;
} else {
time_to_next = base::TimeDelta::FromMilliseconds(
kSyncDelayFastInMilliseconds);
}
timer_.Stop();
}
if (current_delay_ != delay) {
util::Log(logging::LOG_VERBOSE, FROM_HERE,
"[%s] Scheduling task in %" PRId64 " secs",
name_.c_str(), time_to_next.InSeconds());
}
current_delay_ = delay;
timer_.Start(FROM_HERE, time_to_next, this, &SyncProcessRunner::Run);
}
} // namespace sync_file_system