blob: 4657f1a458c97aa7da3af97816b1d3706667a33e [file] [log] [blame]
// Copyright 2014 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 "components/sync/driver/model_load_manager.h"
#include <map>
#include <utility>
#include "base/barrier_closure.h"
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "components/sync/base/model_type.h"
namespace syncer {
ModelLoadManager::ModelLoadManager(
const DataTypeController::TypeMap* controllers,
ModelLoadManagerDelegate* processor)
: controllers_(controllers), delegate_(processor) {}
ModelLoadManager::~ModelLoadManager() = default;
void ModelLoadManager::Initialize(ModelTypeSet desired_types,
ModelTypeSet preferred_types,
const ConfigureContext& context) {
// |desired_types| must be a subset of |preferred_types|.
DCHECK(preferred_types.HasAll(desired_types));
bool sync_mode_changed = configure_context_.sync_mode != context.sync_mode;
configure_context_ = context;
// Only keep types that have controllers.
desired_types_.Clear();
for (ModelType type : desired_types) {
auto dtc_iter = controllers_->find(type);
if (dtc_iter != controllers_->end()) {
const DataTypeController* dtc = dtc_iter->second.get();
// Controllers in a FAILED state should have been filtered out by the
// DataTypeManager.
DCHECK_NE(dtc->state(), DataTypeController::FAILED);
desired_types_.Put(type);
}
}
DVLOG(1) << "ModelLoadManager: Initializing for "
<< ModelTypeSetToString(desired_types_);
notified_about_ready_for_configure_ = false;
DVLOG(1) << "ModelLoadManager: Stopping disabled types.";
std::map<DataTypeController*, ShutdownReason> types_to_stop;
for (const auto& type_and_dtc : *controllers_) {
DataTypeController* dtc = type_and_dtc.second.get();
// We generally stop all data types which are not desired. When the storage
// option changes, we need to restart all data types so that they can
// re-wire to the correct storage.
bool should_stop = !desired_types_.Has(dtc->type()) || sync_mode_changed;
// If the datatype is already STOPPING, we also wait for it to stop, to make
// sure it's ready to start again (if appropriate).
if ((should_stop && dtc->state() != DataTypeController::NOT_RUNNING) ||
dtc->state() == DataTypeController::STOPPING) {
// Note: STOP_SYNC means we'll keep the Sync data around; DISABLE_SYNC
// means we'll clear it.
ShutdownReason reason =
preferred_types.Has(dtc->type()) ? STOP_SYNC : DISABLE_SYNC;
// If we're switching to transport-only mode, don't clear any old data.
// The reason is that if a user temporarily disables Sync, we don't want
// to wipe (and later redownload) all their data, just because Sync
// restarted in transport-only mode.
if (sync_mode_changed &&
configure_context_.sync_mode == SyncMode::kTransportOnly) {
reason = STOP_SYNC;
}
types_to_stop[dtc] = reason;
}
}
// Run LoadDesiredTypes() only after all relevant types are stopped.
// TODO(mastiz): Add test coverage to this waiting logic, including the
// case where the datatype is STOPPING when this function is called.
base::RepeatingClosure barrier_closure = base::BarrierClosure(
types_to_stop.size(), base::BindOnce(&ModelLoadManager::LoadDesiredTypes,
weak_ptr_factory_.GetWeakPtr()));
for (const auto& dtc_and_reason : types_to_stop) {
DataTypeController* dtc = dtc_and_reason.first;
const ShutdownReason reason = dtc_and_reason.second;
DVLOG(1) << "ModelLoadManager: stop " << dtc->name() << " due to "
<< ShutdownReasonToString(reason);
StopDatatypeImpl(SyncError(), reason, dtc, barrier_closure);
}
}
void ModelLoadManager::StopDatatype(ModelType type,
ShutdownReason shutdown_reason,
SyncError error) {
DCHECK(error.IsSet());
desired_types_.Remove(type);
DataTypeController* dtc = controllers_->find(type)->second.get();
if (dtc->state() != DataTypeController::NOT_RUNNING &&
dtc->state() != DataTypeController::STOPPING) {
StopDatatypeImpl(error, shutdown_reason, dtc, base::DoNothing());
}
// Removing a desired type may mean all models are now loaded.
NotifyDelegateIfReadyForConfigure();
}
void ModelLoadManager::StopDatatypeImpl(
const SyncError& error,
ShutdownReason shutdown_reason,
DataTypeController* dtc,
DataTypeController::StopCallback callback) {
loaded_types_.Remove(dtc->type());
DCHECK(error.IsSet() || (dtc->state() != DataTypeController::NOT_RUNNING));
delegate_->OnSingleDataTypeWillStop(dtc->type(), error);
// Note: Depending on |shutdown_reason|, USS types might clear their metadata
// in response to Stop().
dtc->Stop(shutdown_reason, std::move(callback));
}
void ModelLoadManager::LoadDesiredTypes() {
// Note: |desired_types_| might be modified during iteration (e.g. in
// ModelLoadCallback()), so make a copy.
const ModelTypeSet types = desired_types_;
for (ModelType type : types) {
auto dtc_iter = controllers_->find(type);
DCHECK(dtc_iter != controllers_->end());
DataTypeController* dtc = dtc_iter->second.get();
DCHECK_NE(DataTypeController::STOPPING, dtc->state());
if (dtc->state() == DataTypeController::NOT_RUNNING) {
DCHECK(!loaded_types_.Has(dtc->type()));
dtc->LoadModels(configure_context_,
base::BindRepeating(&ModelLoadManager::ModelLoadCallback,
weak_ptr_factory_.GetWeakPtr()));
}
}
// It's possible that all models are already loaded.
NotifyDelegateIfReadyForConfigure();
}
void ModelLoadManager::Stop(ShutdownReason shutdown_reason) {
// Ignore callbacks from controllers.
weak_ptr_factory_.InvalidateWeakPtrs();
// Stop started data types.
for (const auto& type_and_dtc : *controllers_) {
DataTypeController* dtc = type_and_dtc.second.get();
if (dtc->state() != DataTypeController::NOT_RUNNING &&
dtc->state() != DataTypeController::STOPPING) {
// We don't really wait until all datatypes have been fully stopped, which
// is only required (and in fact waited for) when Initialize() is called.
StopDatatypeImpl(SyncError(), shutdown_reason, dtc, base::DoNothing());
DVLOG(1) << "ModelLoadManager: Stopped " << dtc->name();
}
}
desired_types_.Clear();
loaded_types_.Clear();
}
void ModelLoadManager::ModelLoadCallback(ModelType type,
const SyncError& error) {
DVLOG(1) << "ModelLoadManager: ModelLoadCallback for "
<< ModelTypeToString(type);
if (error.IsSet()) {
DVLOG(1) << "ModelLoadManager: Type encountered an error.";
desired_types_.Remove(type);
DataTypeController* dtc = controllers_->find(type)->second.get();
StopDatatypeImpl(error, STOP_SYNC, dtc, base::DoNothing());
NotifyDelegateIfReadyForConfigure();
return;
}
// This happens when slow loading type is disabled by new configuration or
// the model came unready during loading.
if (!desired_types_.Has(type))
return;
DCHECK(!loaded_types_.Has(type));
loaded_types_.Put(type);
NotifyDelegateIfReadyForConfigure();
}
void ModelLoadManager::NotifyDelegateIfReadyForConfigure() {
if (notified_about_ready_for_configure_)
return;
if (!loaded_types_.HasAll(desired_types_)) {
// At least one type is not ready.
return;
}
notified_about_ready_for_configure_ = true;
delegate_->OnAllDataTypesReadyForConfigure();
}
} // namespace syncer