blob: 4023d92746763630dbfe7b9dd8c24d8c20d87d89 [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/async_directory_type_controller.h"
#include <utility>
#include "base/logging.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "components/sync/base/bind_to_task_runner.h"
#include "components/sync/base/data_type_histogram.h"
#include "components/sync/base/model_type.h"
#include "components/sync/driver/configure_context.h"
#include "components/sync/driver/generic_change_processor_factory.h"
#include "components/sync/driver/sync_api_component_factory.h"
#include "components/sync/driver/sync_client.h"
#include "components/sync/driver/sync_service.h"
#include "components/sync/model/data_type_error_handler_impl.h"
#include "components/sync/model/sync_error.h"
#include "components/sync/model/sync_merge_result.h"
#include "components/sync/model/syncable_service.h"
namespace syncer {
SharedChangeProcessor*
AsyncDirectoryTypeController::CreateSharedChangeProcessor() {
return new SharedChangeProcessor(type());
}
AsyncDirectoryTypeController::AsyncDirectoryTypeController(
ModelType type,
const base::Closure& dump_stack,
SyncClient* sync_client,
ModelSafeGroup model_safe_group,
scoped_refptr<base::SequencedTaskRunner> model_thread)
: DirectoryDataTypeController(type,
dump_stack,
sync_client,
model_safe_group),
user_share_(nullptr),
processor_factory_(new GenericChangeProcessorFactory()),
state_(NOT_RUNNING),
model_thread_(std::move(model_thread)) {}
void AsyncDirectoryTypeController::LoadModels(
const ConfigureContext& configure_context,
const ModelLoadCallback& model_load_callback) {
DCHECK(CalledOnValidThread());
DCHECK_EQ(configure_context.storage_option, ConfigureContext::STORAGE_ON_DISK)
<< " for type " << ModelTypeToString(type());
model_load_callback_ = model_load_callback;
if (state() != NOT_RUNNING) {
model_load_callback_.Run(type(),
SyncError(FROM_HERE, SyncError::DATATYPE_ERROR,
"Model already running", type()));
return;
}
state_ = MODEL_STARTING;
// Since we can't be called multiple times before Stop() is called,
// |shared_change_processor_| must be null here.
DCHECK(!shared_change_processor_);
shared_change_processor_ = CreateSharedChangeProcessor();
DCHECK(shared_change_processor_);
if (!StartModels()) {
// If we are waiting for some external service to load before associating
// or we failed to start the models, we exit early.
DCHECK(state() == MODEL_STARTING || state() == NOT_RUNNING);
return;
}
OnModelLoaded();
}
void AsyncDirectoryTypeController::OnModelLoaded() {
DCHECK(CalledOnValidThread());
DCHECK_EQ(state_, MODEL_STARTING);
state_ = MODEL_LOADED;
model_load_callback_.Run(type(), SyncError());
}
bool AsyncDirectoryTypeController::StartModels() {
DCHECK(CalledOnValidThread());
DCHECK_EQ(state_, MODEL_STARTING);
// By default, no additional services need to be started before we can proceed
// with model association.
return true;
}
void AsyncDirectoryTypeController::StopModels() {
DCHECK(CalledOnValidThread());
}
bool AsyncDirectoryTypeController::PostTaskOnModelThread(
const base::Location& from_here,
const base::Closure& task) {
DCHECK(CalledOnValidThread());
return model_thread_->PostTask(from_here, task);
}
void AsyncDirectoryTypeController::StartAssociating(
StartCallback start_callback) {
DCHECK(CalledOnValidThread());
DCHECK(!start_callback.is_null());
DCHECK_EQ(state_, MODEL_LOADED);
state_ = ASSOCIATING;
// Store UserShare now while on UI thread to avoid potential race
// condition in StartAssociationWithSharedChangeProcessor.
DCHECK(sync_client_->GetSyncService());
user_share_ = sync_client_->GetSyncService()->GetUserShare();
start_callback_ = std::move(start_callback);
if (!StartAssociationAsync()) {
SyncError error(FROM_HERE, SyncError::DATATYPE_ERROR,
"Failed to post StartAssociation", type());
SyncMergeResult local_merge_result(type());
local_merge_result.set_error(error);
StartDone(ASSOCIATION_FAILED, local_merge_result, SyncMergeResult(type()));
// StartDone should have cleared the SharedChangeProcessor.
DCHECK(!shared_change_processor_);
return;
}
}
void AsyncDirectoryTypeController::Stop(ShutdownReason shutdown_reason) {
DCHECK(CalledOnValidThread());
if (state() == NOT_RUNNING)
return;
// Disconnect the change processor. At this point, the
// SyncableService can no longer interact with the Syncer, even if
// it hasn't finished MergeDataAndStartSyncing.
DisconnectSharedChangeProcessor();
// If we haven't finished starting, we need to abort the start.
bool service_started = state() == ASSOCIATING || state() == RUNNING;
state_ = service_started ? STOPPING : NOT_RUNNING;
StopModels();
if (service_started)
StopSyncableService();
shared_change_processor_ = nullptr;
state_ = NOT_RUNNING;
}
DataTypeController::State AsyncDirectoryTypeController::state() const {
return state_;
}
void AsyncDirectoryTypeController::SetGenericChangeProcessorFactoryForTest(
std::unique_ptr<GenericChangeProcessorFactory> factory) {
DCHECK_EQ(state_, NOT_RUNNING);
processor_factory_ = std::move(factory);
}
AsyncDirectoryTypeController::AsyncDirectoryTypeController()
: DirectoryDataTypeController(UNSPECIFIED,
base::Closure(),
nullptr,
GROUP_PASSIVE) {}
AsyncDirectoryTypeController::~AsyncDirectoryTypeController() {}
void AsyncDirectoryTypeController::StartDone(
DataTypeController::ConfigureResult start_result,
const SyncMergeResult& local_merge_result,
const SyncMergeResult& syncer_merge_result) {
DCHECK(CalledOnValidThread());
DataTypeController::State new_state;
if (IsSuccessfulResult(start_result)) {
new_state = RUNNING;
} else {
new_state = (start_result == ASSOCIATION_FAILED ? FAILED : NOT_RUNNING);
}
// If we failed to start up, and we haven't been stopped yet, we need to
// ensure we clean up the local service and shared change processor properly.
if (new_state != RUNNING && state() != NOT_RUNNING && state() != STOPPING) {
DisconnectSharedChangeProcessor();
StopSyncableService();
shared_change_processor_ = nullptr;
}
// It's possible to have StartDone called first from the UI thread
// (due to Stop being called) and then posted from the non-UI thread. In
// this case, we drop the second call because we've already been stopped.
if (state_ == NOT_RUNNING) {
return;
}
state_ = new_state;
if (state_ != RUNNING) {
// Start failed.
StopModels();
RecordStartFailure(start_result);
}
std::move(start_callback_)
.Run(start_result, local_merge_result, syncer_merge_result);
}
void AsyncDirectoryTypeController::RecordStartFailure(ConfigureResult result) {
DCHECK(CalledOnValidThread());
// TODO(wychen): enum uma should be strongly typed. crbug.com/661401
UMA_HISTOGRAM_ENUMERATION("Sync.DataTypeStartFailures2",
ModelTypeToHistogramInt(type()),
static_cast<int>(MODEL_TYPE_COUNT));
#define PER_DATA_TYPE_MACRO(type_str) \
UMA_HISTOGRAM_ENUMERATION("Sync." type_str "ConfigureFailure", result, \
MAX_CONFIGURE_RESULT);
SYNC_DATA_TYPE_HISTOGRAM(type());
#undef PER_DATA_TYPE_MACRO
}
void AsyncDirectoryTypeController::DisableImpl(const SyncError& error) {
DCHECK(CalledOnValidThread());
if (model_load_callback_) {
model_load_callback_.Run(type(), error);
}
}
bool AsyncDirectoryTypeController::StartAssociationAsync() {
DCHECK(CalledOnValidThread());
DCHECK_EQ(state(), ASSOCIATING);
return PostTaskOnModelThread(
FROM_HERE,
base::Bind(
&SharedChangeProcessor::StartAssociation, shared_change_processor_,
BindToCurrentSequence(base::Bind(
&AsyncDirectoryTypeController::StartDone, base::AsWeakPtr(this))),
sync_client_, processor_factory_.get(), user_share_,
base::Passed(CreateErrorHandler())));
}
ChangeProcessor* AsyncDirectoryTypeController::GetChangeProcessor() const {
DCHECK(CalledOnValidThread());
DCHECK_EQ(state_, RUNNING);
return shared_change_processor_->generic_change_processor();
}
void AsyncDirectoryTypeController::DisconnectSharedChangeProcessor() {
DCHECK(CalledOnValidThread());
// |shared_change_processor_| can already be null if Stop() is
// called after StartDone(_, FAILED, _).
if (shared_change_processor_) {
shared_change_processor_->Disconnect();
}
}
void AsyncDirectoryTypeController::StopSyncableService() {
DCHECK(CalledOnValidThread());
if (shared_change_processor_) {
PostTaskOnModelThread(FROM_HERE,
base::Bind(&SharedChangeProcessor::StopLocalService,
shared_change_processor_));
}
}
std::unique_ptr<DataTypeErrorHandler>
AsyncDirectoryTypeController::CreateErrorHandler() {
DCHECK(CalledOnValidThread());
return std::make_unique<DataTypeErrorHandlerImpl>(
base::SequencedTaskRunnerHandle::Get(), dump_stack_,
base::Bind(&AsyncDirectoryTypeController::DisableImpl,
base::AsWeakPtr(this)));
}
} // namespace syncer