| Index: chrome/browser/sync/glue/data_type_manager_impl.cc
|
| diff --git a/chrome/browser/sync/glue/data_type_manager_impl.cc b/chrome/browser/sync/glue/data_type_manager_impl.cc
|
| index 12cd4d2872f5c9126063bf36e12e870a2ee7ecf2..a166d11ddb04e08fc53139668c2fcb7d2d5201f9 100644
|
| --- a/chrome/browser/sync/glue/data_type_manager_impl.cc
|
| +++ b/chrome/browser/sync/glue/data_type_manager_impl.cc
|
| @@ -2,16 +2,20 @@
|
| // Use of this source code is governed by a BSD-style license that can be
|
| // found in the LICENSE file.
|
|
|
| +#include <algorithm>
|
| +#include <functional>
|
| +
|
| #include "base/logging.h"
|
| #include "base/task.h"
|
| #include "chrome/browser/chrome_thread.h"
|
| +#include "chrome/browser/sync/glue/data_type_controller.h"
|
| #include "chrome/browser/sync/glue/data_type_manager_impl.h"
|
| #include "chrome/browser/sync/glue/sync_backend_host.h"
|
| #include "chrome/common/notification_details.h"
|
| #include "chrome/common/notification_service.h"
|
| #include "chrome/common/notification_source.h"
|
|
|
| -static const int kStartOrderStart = -1;
|
| +namespace browser_sync {
|
|
|
| namespace {
|
|
|
| @@ -22,9 +26,24 @@ static const syncable::ModelType kStartOrder[] = {
|
| syncable::TYPED_URLS,
|
| };
|
|
|
| -} // namespace
|
| +// Comparator used when sorting data type controllers.
|
| +class SortComparator : public std::binary_function<DataTypeController*,
|
| + DataTypeController*,
|
| + bool> {
|
| + public:
|
| + explicit SortComparator(std::map<syncable::ModelType, int>* order)
|
| + : order_(order) { }
|
|
|
| -namespace browser_sync {
|
| + // Returns true if lhs preceeds rhs.
|
| + bool operator() (DataTypeController* lhs, DataTypeController* rhs) {
|
| + return (*order_)[lhs->type()] < (*order_)[rhs->type()];
|
| + }
|
| +
|
| + private:
|
| + std::map<syncable::ModelType, int>* order_;
|
| +};
|
| +
|
| +} // namespace
|
|
|
| DataTypeManagerImpl::DataTypeManagerImpl(
|
| SyncBackendHost* backend,
|
| @@ -32,68 +51,125 @@ DataTypeManagerImpl::DataTypeManagerImpl(
|
| : backend_(backend),
|
| controllers_(controllers),
|
| state_(DataTypeManager::STOPPED),
|
| - current_type_(kStartOrderStart) {
|
| + current_dtc_(NULL) {
|
| DCHECK(backend_);
|
| - DCHECK(arraysize(kStartOrder) > 0);
|
| + DCHECK_GT(arraysize(kStartOrder), 0U);
|
| // Ensure all data type controllers are stopped.
|
| for (DataTypeController::TypeMap::const_iterator it = controllers_.begin();
|
| it != controllers_.end(); ++it) {
|
| DCHECK_EQ(DataTypeController::NOT_RUNNING, (*it).second->state());
|
| }
|
| +
|
| + // Build a ModelType -> order map for sorting.
|
| + for (int i = 0; i < static_cast<int>(arraysize(kStartOrder)); i++)
|
| + start_order_[kStartOrder[i]] = i;
|
| }
|
|
|
| -void DataTypeManagerImpl::Start(StartCallback* start_callback) {
|
| +void DataTypeManagerImpl::Configure(const TypeSet& desired_types) {
|
| DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
|
| - if (state_ != STOPPED) {
|
| - start_callback->Run(BUSY);
|
| - delete start_callback;
|
| + if (state_ == STOPPING) {
|
| + // You can not set a configuration while stopping.
|
| + LOG(ERROR) << "Configuration set while stopping.";
|
| return;
|
| }
|
|
|
| - state_ = PAUSE_PENDING;
|
| - start_callback_.reset(start_callback);
|
| - current_type_ = kStartOrderStart;
|
| + // Add any data type controllers into the needs_start_ list that are
|
| + // currently NOT_RUNNING or STOPPING.
|
| + needs_start_.clear();
|
| + for (TypeSet::const_iterator it = desired_types.begin();
|
| + it != desired_types.end(); ++it) {
|
| + DataTypeController* dtc = controllers_[*it];
|
| + if (dtc && (dtc->state() == DataTypeController::NOT_RUNNING ||
|
| + dtc->state() == DataTypeController::STOPPING)) {
|
| + needs_start_.push_back(dtc);
|
| + LOG(INFO) << "Will start " << dtc->name();
|
| + }
|
| + }
|
| + // Sort these according to kStartOrder.
|
| + std::sort(needs_start_.begin(),
|
| + needs_start_.end(),
|
| + SortComparator(&start_order_));
|
|
|
| - // Pause the sync backend before starting the data types.
|
| - AddObserver(NotificationType::SYNC_PAUSED);
|
| - if (!backend_->RequestPause()) {
|
| - RemoveObserver(NotificationType::SYNC_PAUSED);
|
| - state_ = STOPPED;
|
| - start_callback_->Run(UNRECOVERABLE_ERROR);
|
| - start_callback_.reset();
|
| + // Add any data type controllers into that needs_stop_ list that are
|
| + // currently MODEL_STARTING, ASSOCIATING, or RUNNING.
|
| + needs_stop_.clear();
|
| + for (DataTypeController::TypeMap::const_iterator it = controllers_.begin();
|
| + it != controllers_.end(); ++it) {
|
| + DataTypeController* dtc = (*it).second;
|
| + if (desired_types.count(dtc->type()) == 0 && (
|
| + dtc->state() == DataTypeController::MODEL_STARTING ||
|
| + dtc->state() == DataTypeController::ASSOCIATING ||
|
| + dtc->state() == DataTypeController::RUNNING)) {
|
| + needs_stop_.push_back(dtc);
|
| + LOG(INFO) << "Will stop " << dtc->name();
|
| + }
|
| }
|
| + // Sort these according to kStartOrder.
|
| + std::sort(needs_stop_.begin(),
|
| + needs_stop_.end(),
|
| + SortComparator(&start_order_));
|
| +
|
| + // If nothing changed, we're done.
|
| + if (needs_start_.size() == 0 && needs_stop_.size() == 0) {
|
| + state_ = CONFIGURED;
|
| + NotifyStart();
|
| + NotifyDone(OK);
|
| + return;
|
| + }
|
| +
|
| + Restart();
|
| +}
|
| +
|
| +void DataTypeManagerImpl::Restart() {
|
| + LOG(INFO) << "Restarting...";
|
| + // If we are currently waiting for an asynchronous process to
|
| + // complete, change our state to RESTARTING so those processes know
|
| + // that we want to start over when they finish.
|
| + if (state_ == DOWNLOAD_PENDING || state_ == PAUSE_PENDING ||
|
| + state_ == CONFIGURING || state_ == RESUME_PENDING) {
|
| + state_ = RESTARTING;
|
| + return;
|
| + }
|
| +
|
| + DCHECK(state_ == STOPPED || state_ == RESTARTING || state_ == CONFIGURED);
|
| + current_dtc_ = NULL;
|
| +
|
| + // Starting from a "steady state" (stopped or configured) state
|
| + // should send a start notification.
|
| + if (state_ == STOPPED || state_ == CONFIGURED)
|
| + NotifyStart();
|
| +
|
| + // Stop requested data types.
|
| + for (size_t i = 0; i < needs_stop_.size(); ++i) {
|
| + LOG(INFO) << "Stopping " << needs_stop_[i]->name();
|
| + needs_stop_[i]->Stop();
|
| + }
|
| + needs_stop_.clear();
|
| +
|
| + // TODO(sync): Get updates for new data types here.
|
| +
|
| + // Pause the sync backend before starting the data types.
|
| + state_ = PAUSE_PENDING;
|
| + PauseSyncer();
|
| }
|
|
|
| void DataTypeManagerImpl::StartNextType() {
|
| - // Ensure that the current type has indeed started.
|
| - DCHECK(current_type_ == kStartOrderStart ||
|
| - controllers_[kStartOrder[current_type_]]->state() ==
|
| - DataTypeController::RUNNING);
|
| -
|
| - // Find the next startable type.
|
| - while (current_type_ < static_cast<int>(arraysize(kStartOrder)) - 1) {
|
| - current_type_++;
|
| - syncable::ModelType type = kStartOrder[current_type_];
|
| - if (IsEnabled(type)) {
|
| - LOG(INFO) << "Starting " << controllers_[type]->name();
|
| - controllers_[type]->Start(
|
| - true,
|
| - NewCallback(this, &DataTypeManagerImpl::TypeStartCallback));
|
| - return;
|
| - }
|
| + // If there are any data types left to start, start the one at the
|
| + // front of the list.
|
| + if (needs_start_.size() > 0) {
|
| + current_dtc_ = needs_start_[0];
|
| + LOG(INFO) << "Starting " << current_dtc_->name();
|
| + current_dtc_->Start(
|
| + true,
|
| + NewCallback(this, &DataTypeManagerImpl::TypeStartCallback));
|
| + return;
|
| }
|
|
|
| - // No more startable types found, we must be done. Resume the sync
|
| + // If no more data types need starting, we're done. Resume the sync
|
| // backend to finish.
|
| - DCHECK_EQ(state_, STARTING);
|
| - AddObserver(NotificationType::SYNC_RESUMED);
|
| + DCHECK_EQ(state_, CONFIGURING);
|
| state_ = RESUME_PENDING;
|
| - if (!backend_->RequestResume()) {
|
| - RemoveObserver(NotificationType::SYNC_RESUMED);
|
| - FinishStop();
|
| - start_callback_->Run(UNRECOVERABLE_ERROR);
|
| - start_callback_.reset();
|
| - }
|
| + ResumeSyncer();
|
| }
|
|
|
| void DataTypeManagerImpl::TypeStartCallback(
|
| @@ -101,22 +177,43 @@ void DataTypeManagerImpl::TypeStartCallback(
|
| // When the data type controller invokes this callback, it must be
|
| // on the UI thread.
|
| DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
|
| + DCHECK(current_dtc_);
|
| +
|
| + // If configuration changed while this data type was starting, we
|
| + // need to reset. Resume the syncer.
|
| + if (state_ == RESTARTING) {
|
| + ResumeSyncer();
|
| + return;
|
| + }
|
|
|
| - // If we reach this callback while stopping, this means that the
|
| - // current data type was stopped while still starting up. Now that
|
| - // the data type is aborted, we can finish stop.
|
| + // We're done with the data type at the head of the list -- remove it.
|
| + DataTypeController* started_dtc = current_dtc_;
|
| + DCHECK(needs_start_.size());
|
| + DCHECK_EQ(needs_start_[0], started_dtc);
|
| + needs_start_.erase(needs_start_.begin());
|
| + current_dtc_ = NULL;
|
| +
|
| + // If we reach this callback while stopping, this means that
|
| + // DataTypeManager::Stop() was called while the current data type
|
| + // was starting. Now that it has finished starting, we can finish
|
| + // stopping the DataTypeManager. This is considered an ABORT.
|
| if (state_ == STOPPING) {
|
| FinishStop();
|
| - start_callback_->Run(DataTypeManager::ABORTED);
|
| - start_callback_.reset();
|
| + NotifyDone(ABORTED);
|
| + return;
|
| + }
|
| +
|
| + // If our state_ is STOPPED, we have already stopped all of the data
|
| + // types. We should not be getting callbacks from stopped data types.
|
| + if (state_ == STOPPED) {
|
| + LOG(ERROR) << "Start callback called by stopped data type!";
|
| return;
|
| }
|
|
|
| // If the type started normally, continue to the next type.
|
| - syncable::ModelType type = kStartOrder[current_type_];
|
| if (result == DataTypeController::OK ||
|
| result == DataTypeController::OK_FIRST_RUN) {
|
| - LOG(INFO) << "Started " << controllers_[type]->name();
|
| + LOG(INFO) << "Started " << started_dtc->name();
|
| StartNextType();
|
| return;
|
| }
|
| @@ -124,25 +221,24 @@ void DataTypeManagerImpl::TypeStartCallback(
|
| // Any other result is a fatal error. Shut down any types we've
|
| // managed to start up to this point and pass the result to the
|
| // callback.
|
| - LOG(INFO) << "Failed " << controllers_[type]->name();
|
| + LOG(INFO) << "Failed " << started_dtc->name();
|
| FinishStop();
|
| - StartResult start_result = DataTypeManager::ABORTED;
|
| + ConfigureResult configure_result = DataTypeManager::ABORTED;
|
| switch (result) {
|
| case DataTypeController::ABORTED:
|
| - start_result = DataTypeManager::ABORTED;
|
| + configure_result = DataTypeManager::ABORTED;
|
| break;
|
| case DataTypeController::ASSOCIATION_FAILED:
|
| - start_result = DataTypeManager::ASSOCIATION_FAILED;
|
| + configure_result = DataTypeManager::ASSOCIATION_FAILED;
|
| break;
|
| case DataTypeController::UNRECOVERABLE_ERROR:
|
| - start_result = DataTypeManager::UNRECOVERABLE_ERROR;
|
| + configure_result = DataTypeManager::UNRECOVERABLE_ERROR;
|
| break;
|
| default:
|
| NOTREACHED();
|
| break;
|
| }
|
| - start_callback_->Run(start_result);
|
| - start_callback_.reset();
|
| + NotifyDone(configure_result);
|
| }
|
|
|
| void DataTypeManagerImpl::Stop() {
|
| @@ -150,58 +246,73 @@ void DataTypeManagerImpl::Stop() {
|
| if (state_ == STOPPED)
|
| return;
|
|
|
| - // If we are currently starting, then the current type is in a
|
| - // partially started state. Abort the startup of the current type
|
| - // and continue shutdown when the abort completes.
|
| - if (state_ == STARTING) {
|
| + // If we are currently configuring, then the current type is in a
|
| + // partially started state. Abort the startup of the current type,
|
| + // which will synchronously invoke the start callback.
|
| + if (state_ == CONFIGURING) {
|
| state_ = STOPPING;
|
| - syncable::ModelType type = kStartOrder[current_type_];
|
| - controllers_[type]->Stop();
|
| + current_dtc_->Stop();
|
| return;
|
| }
|
|
|
| + // If Stop() is called while waiting for pause or resume, we no
|
| + // longer care about this.
|
| + if (state_ == PAUSE_PENDING)
|
| + RemoveObserver(NotificationType::SYNC_PAUSED);
|
| + if (state_ == RESUME_PENDING)
|
| + RemoveObserver(NotificationType::SYNC_RESUMED);
|
| +
|
| state_ = STOPPING;
|
| FinishStop();
|
| }
|
|
|
| void DataTypeManagerImpl::FinishStop() {
|
| - DCHECK(state_== STARTING || state_ == STOPPING || state_ == RESUME_PENDING);
|
| + DCHECK(state_== CONFIGURING ||
|
| + state_ == STOPPING ||
|
| + state_ == PAUSE_PENDING ||
|
| + state_ == RESUME_PENDING);
|
| // Simply call the Stop() method on all running data types.
|
| - for (unsigned int i = 0; i < arraysize(kStartOrder); ++i) {
|
| - syncable::ModelType type = kStartOrder[i];
|
| - if (IsRegistered(type) &&
|
| - controllers_[type]->state() == DataTypeController::RUNNING) {
|
| - controllers_[type]->Stop();
|
| - LOG(INFO) << "Stopped " << controllers_[type]->name();
|
| + for (DataTypeController::TypeMap::const_iterator it = controllers_.begin();
|
| + it != controllers_.end(); ++it) {
|
| + DataTypeController* dtc = (*it).second;
|
| + if (dtc->state() == DataTypeController::RUNNING) {
|
| + dtc->Stop();
|
| + LOG(INFO) << "Stopped " << dtc->name();
|
| }
|
| }
|
| state_ = STOPPED;
|
| }
|
|
|
| -bool DataTypeManagerImpl::IsRegistered(syncable::ModelType type) {
|
| - return controllers_.count(type) == 1;
|
| -}
|
| -
|
| -bool DataTypeManagerImpl::IsEnabled(syncable::ModelType type) {
|
| - return IsRegistered(type) && controllers_[type]->enabled();
|
| -}
|
| -
|
| void DataTypeManagerImpl::Observe(NotificationType type,
|
| const NotificationSource& source,
|
| const NotificationDetails& details) {
|
| - switch(type.value) {
|
| + switch (type.value) {
|
| case NotificationType::SYNC_PAUSED:
|
| - DCHECK_EQ(state_, PAUSE_PENDING);
|
| - state_ = STARTING;
|
| + DCHECK(state_ == PAUSE_PENDING || state_ == RESTARTING);
|
| RemoveObserver(NotificationType::SYNC_PAUSED);
|
| +
|
| + // If the state changed to RESTARTING while waiting to be
|
| + // paused, resume the syncer so we can restart.
|
| + if (state_ == RESTARTING) {
|
| + ResumeSyncer();
|
| + return;
|
| + }
|
| +
|
| + state_ = CONFIGURING;
|
| StartNextType();
|
| break;
|
| case NotificationType::SYNC_RESUMED:
|
| - DCHECK_EQ(state_, RESUME_PENDING);
|
| - state_ = STARTED;
|
| + DCHECK(state_ == RESUME_PENDING || state_ == RESTARTING);
|
| RemoveObserver(NotificationType::SYNC_RESUMED);
|
| - start_callback_->Run(OK);
|
| - start_callback_.reset();
|
| +
|
| + // If we are resuming because of a restart, continue the restart.
|
| + if (state_ == RESTARTING) {
|
| + Restart();
|
| + return;
|
| + }
|
| +
|
| + state_ = CONFIGURED;
|
| + NotifyDone(OK);
|
| break;
|
| default:
|
| NOTREACHED();
|
| @@ -220,4 +331,36 @@ void DataTypeManagerImpl::RemoveObserver(NotificationType type) {
|
| NotificationService::AllSources());
|
| }
|
|
|
| +void DataTypeManagerImpl::NotifyStart() {
|
| + NotificationService::current()->Notify(
|
| + NotificationType::SYNC_CONFIGURE_START,
|
| + NotificationService::AllSources(),
|
| + NotificationService::NoDetails());
|
| +}
|
| +
|
| +void DataTypeManagerImpl::NotifyDone(ConfigureResult result) {
|
| + NotificationService::current()->Notify(
|
| + NotificationType::SYNC_CONFIGURE_DONE,
|
| + NotificationService::AllSources(),
|
| + Details<ConfigureResult>(&result));
|
| +}
|
| +
|
| +void DataTypeManagerImpl::ResumeSyncer() {
|
| + AddObserver(NotificationType::SYNC_RESUMED);
|
| + if (!backend_->RequestResume()) {
|
| + RemoveObserver(NotificationType::SYNC_RESUMED);
|
| + FinishStop();
|
| + NotifyDone(UNRECOVERABLE_ERROR);
|
| + }
|
| +}
|
| +
|
| +void DataTypeManagerImpl::PauseSyncer() {
|
| + AddObserver(NotificationType::SYNC_PAUSED);
|
| + if (!backend_->RequestPause()) {
|
| + RemoveObserver(NotificationType::SYNC_PAUSED);
|
| + FinishStop();
|
| + NotifyDone(UNRECOVERABLE_ERROR);
|
| + }
|
| +}
|
| +
|
| } // namespace browser_sync
|
|
|