Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(281)

Unified Diff: chrome/browser/sync/glue/data_type_manager_impl.cc

Issue 1128012: Rewrite DTM to support dynamic data type configuration (Closed)
Patch Set: Address review comments and fix a leak. Created 10 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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
« no previous file with comments | « chrome/browser/sync/glue/data_type_manager_impl.h ('k') | chrome/browser/sync/glue/data_type_manager_impl_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698