blob: 7bd84e6e383d9d9b5ee8c9ac168b9b011fa11fd2 [file] [log] [blame]
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/sync/driver/startup_controller.h"
#include <utility>
#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/string_number_conversions.h"
#include "base/task/sequenced_task_runner.h"
#include "components/sync/base/command_line_switches.h"
#include "components/sync/base/features.h"
namespace syncer {
namespace {
// The amount of time we'll wait to initialize sync if no data type requests
// immediately initialization.
constexpr base::TimeDelta kDefaultDeferredInitDelay = base::Seconds(10);
base::TimeDelta GetDeferredInitDelay() {
if (base::FeatureList::IsEnabled(kDeferredSyncStartupCustomDelay)) {
return base::Seconds(kDeferredSyncStartupCustomDelayInSeconds.Get());
}
const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
if (cmdline->HasSwitch(kSyncDeferredStartupTimeoutSeconds)) {
int timeout = 0;
if (base::StringToInt(
cmdline->GetSwitchValueASCII(kSyncDeferredStartupTimeoutSeconds),
&timeout)) {
DCHECK_GE(timeout, 0);
DVLOG(2) << "Sync StartupController overriding startup timeout to "
<< timeout << " seconds.";
return base::Seconds(timeout);
}
}
return kDefaultDeferredInitDelay;
}
} // namespace
StartupController::StartupController(
base::RepeatingCallback<ModelTypeSet()> get_preferred_data_types,
base::RepeatingCallback<bool()> should_start,
base::OnceClosure start_engine)
: get_preferred_data_types_callback_(std::move(get_preferred_data_types)),
should_start_callback_(std::move(should_start)),
start_engine_callback_(std::move(start_engine)) {}
StartupController::~StartupController() = default;
void StartupController::TryStart(bool force_immediate) {
// Post a task instead of running the startup checks directly, to guarantee
// that |start_engine_callback_| is never called synchronously from
// TryStart().
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(&StartupController::TryStartImpl,
weak_factory_.GetWeakPtr(), force_immediate));
}
void StartupController::TryStartImpl(bool force_immediate) {
if (!should_start_callback_.Run()) {
return;
}
const bool first_start = start_up_time_.is_null();
if (first_start) {
start_up_time_ = base::Time::Now();
}
// For performance reasons, defer the heavy lifting for sync init unless:
//
// - a datatype has requested an immediate start of sync, or
// - sync needs to start up the engine immediately to provide control state
// and encryption information to the UI.
if (!force_immediate && !bypass_deferred_startup_ &&
get_preferred_data_types_callback_.Run().Has(SESSIONS)) {
if (first_start) {
base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&StartupController::OnFallbackStartupTimerExpired,
weak_factory_.GetWeakPtr()),
GetDeferredInitDelay());
}
return;
}
if (start_engine_callback_) {
std::move(start_engine_callback_).Run();
}
}
void StartupController::RecordTimeDeferred(DeferredInitTrigger trigger) {
DCHECK(!start_up_time_.is_null());
base::TimeDelta time_deferred = base::Time::Now() - start_up_time_;
base::UmaHistogramCustomTimes("Sync.Startup.TimeDeferred2", time_deferred,
base::Seconds(0), base::Minutes(2), 60);
base::UmaHistogramEnumeration("Sync.Startup.DeferredInitTrigger", trigger);
}
void StartupController::OnFallbackStartupTimerExpired() {
if (!start_engine_callback_) {
return;
}
DVLOG(2) << "Sync deferred init fallback timer expired, starting engine.";
RecordTimeDeferred(DeferredInitTrigger::kFallbackTimer);
// Once the deferred init timer has expired, don't defer startup again (until
// Reset() or browser restart), even if this startup attempt doesn't succeed.
bypass_deferred_startup_ = true;
TryStart(/*force_immediate=*/false);
}
StartupController::State StartupController::GetState() const {
if (!start_engine_callback_) {
return State::STARTED;
}
if (!start_up_time_.is_null()) {
return State::STARTING_DEFERRED;
}
return State::NOT_STARTED;
}
void StartupController::OnDataTypeRequestsSyncStartup(ModelType type) {
if (!start_engine_callback_) {
return;
}
DVLOG(2) << "Data type requesting sync startup: "
<< ModelTypeToDebugString(type);
if (!start_up_time_.is_null()) {
RecordTimeDeferred(DeferredInitTrigger::kDataTypeRequest);
}
bypass_deferred_startup_ = true;
TryStart(/*force_immediate=*/false);
}
} // namespace syncer