| // 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<bool()> should_start, |
| base::OnceClosure start_engine) |
| : 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_) { |
| 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 |