Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/browser/sync/glue/data_type_manager_impl2.h" | |
| 6 | |
| 7 #include "base/compiler_specific.h" | |
| 8 #include "base/logging.h" | |
| 9 #include "chrome/browser/browser_thread.h" | |
| 10 #include "chrome/browser/sync/glue/data_type_controller.h" | |
| 11 #include "chrome/browser/sync/glue/sync_backend_host.h" | |
| 12 #include "chrome/common/notification_details.h" | |
| 13 #include "chrome/common/notification_service.h" | |
| 14 #include "chrome/common/notification_source.h" | |
| 15 | |
| 16 namespace browser_sync { | |
| 17 | |
| 18 namespace { | |
| 19 | |
| 20 static const syncable::ModelType kStartOrder[] = { | |
| 21 syncable::NIGORI, | |
|
Nicolas Zea
2011/02/16 18:40:53
Presumably this is just for the sake of being comp
tim (not reviewing)
2011/02/17 21:27:25
Done.
| |
| 22 syncable::BOOKMARKS, | |
| 23 syncable::PREFERENCES, | |
| 24 syncable::AUTOFILL, | |
| 25 syncable::AUTOFILL_PROFILE, | |
| 26 syncable::EXTENSIONS, | |
| 27 syncable::APPS, | |
| 28 syncable::THEMES, | |
| 29 syncable::TYPED_URLS, | |
| 30 syncable::PASSWORDS, | |
| 31 syncable::SESSIONS, | |
| 32 }; | |
| 33 | |
| 34 COMPILE_ASSERT(arraysize(kStartOrder) == | |
| 35 syncable::MODEL_TYPE_COUNT - syncable::FIRST_REAL_MODEL_TYPE, | |
| 36 kStartOrder_IncorrectSize); | |
| 37 | |
| 38 // Comparator used when sorting data type controllers. | |
| 39 class SortComparator : public std::binary_function<DataTypeController*, | |
| 40 DataTypeController*, | |
| 41 bool> { | |
| 42 public: | |
| 43 explicit SortComparator(std::map<syncable::ModelType, int>* order) | |
| 44 : order_(order) { } | |
| 45 | |
| 46 // Returns true if lhs precedes rhs. | |
| 47 bool operator() (DataTypeController* lhs, DataTypeController* rhs) { | |
| 48 return (*order_)[lhs->type()] < (*order_)[rhs->type()]; | |
| 49 } | |
| 50 | |
| 51 private: | |
| 52 std::map<syncable::ModelType, int>* order_; | |
| 53 }; | |
| 54 | |
| 55 } // namespace | |
| 56 | |
| 57 DataTypeManagerImpl2::DataTypeManagerImpl2(SyncBackendHost* backend, | |
| 58 const DataTypeController::TypeMap& controllers) | |
| 59 : backend_(backend), | |
| 60 controllers_(controllers), | |
| 61 state_(DataTypeManager::STOPPED), | |
| 62 current_dtc_(NULL), | |
| 63 method_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { | |
| 64 DCHECK(backend_); | |
| 65 // Ensure all data type controllers are stopped. | |
| 66 for (DataTypeController::TypeMap::const_iterator it = controllers_.begin(); | |
| 67 it != controllers_.end(); ++it) { | |
| 68 DCHECK_EQ(DataTypeController::NOT_RUNNING, (*it).second->state()); | |
| 69 } | |
| 70 | |
| 71 // Build a ModelType -> order map for sorting. | |
| 72 for (int i = 0; i < static_cast<int>(arraysize(kStartOrder)); i++) | |
| 73 start_order_[kStartOrder[i]] = i; | |
| 74 } | |
| 75 | |
| 76 DataTypeManagerImpl2::~DataTypeManagerImpl2() {} | |
| 77 | |
| 78 void DataTypeManagerImpl2::Configure(const TypeSet& desired_types) { | |
| 79 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 80 if (state_ == STOPPING) { | |
| 81 // You can not set a configuration while stopping. | |
| 82 LOG(ERROR) << "Configuration set while stopping."; | |
| 83 return; | |
| 84 } | |
| 85 | |
| 86 last_requested_types_ = desired_types; | |
| 87 // Add any data type controllers into the needs_start_ list that are | |
| 88 // currently NOT_RUNNING or STOPPING. | |
| 89 needs_start_.clear(); | |
| 90 for (TypeSet::const_iterator it = desired_types.begin(); | |
| 91 it != desired_types.end(); ++it) { | |
| 92 DataTypeController::TypeMap::const_iterator dtc = controllers_.find(*it); | |
| 93 if (dtc != controllers_.end() && | |
| 94 (dtc->second->state() == DataTypeController::NOT_RUNNING || | |
| 95 dtc->second->state() == DataTypeController::STOPPING)) { | |
| 96 needs_start_.push_back(dtc->second.get()); | |
| 97 VLOG(1) << "Will start " << dtc->second->name(); | |
| 98 } | |
| 99 } | |
| 100 // Sort these according to kStartOrder. | |
| 101 std::sort(needs_start_.begin(), | |
| 102 needs_start_.end(), | |
| 103 SortComparator(&start_order_)); | |
| 104 | |
| 105 // Add any data type controllers into that needs_stop_ list that are | |
| 106 // currently MODEL_STARTING, ASSOCIATING, or RUNNING. | |
| 107 needs_stop_.clear(); | |
| 108 for (DataTypeController::TypeMap::const_iterator it = controllers_.begin(); | |
| 109 it != controllers_.end(); ++it) { | |
| 110 DataTypeController* dtc = (*it).second; | |
| 111 if (desired_types.count(dtc->type()) == 0 && ( | |
| 112 dtc->state() == DataTypeController::MODEL_STARTING || | |
| 113 dtc->state() == DataTypeController::ASSOCIATING || | |
| 114 dtc->state() == DataTypeController::RUNNING)) { | |
| 115 needs_stop_.push_back(dtc); | |
| 116 VLOG(1) << "Will stop " << dtc->name(); | |
| 117 } | |
| 118 } | |
| 119 // Sort these according to kStartOrder. | |
| 120 std::sort(needs_stop_.begin(), | |
| 121 needs_stop_.end(), | |
| 122 SortComparator(&start_order_)); | |
| 123 | |
| 124 // If nothing changed, we're done. | |
| 125 if (needs_start_.size() == 0 && needs_stop_.size() == 0) { | |
| 126 state_ = CONFIGURED; | |
| 127 NotifyStart(); | |
| 128 NotifyDone(OK); | |
| 129 return; | |
| 130 } | |
| 131 | |
| 132 Restart(); | |
| 133 } | |
| 134 | |
| 135 void DataTypeManagerImpl2::Restart() { | |
| 136 VLOG(1) << "Restarting..."; | |
| 137 | |
| 138 // If we are currently waiting for an asynchronous process to | |
| 139 // complete, change our state to RESTARTING so those processes know | |
| 140 // that we want to start over when they finish. | |
| 141 if (state_ == DOWNLOAD_PENDING || state_ == CONFIGURING) { | |
| 142 state_ = RESTARTING; | |
| 143 return; | |
| 144 } | |
| 145 | |
| 146 DCHECK(state_ == STOPPED || state_ == RESTARTING || state_ == CONFIGURED); | |
| 147 current_dtc_ = NULL; | |
| 148 | |
| 149 // Starting from a "steady state" (stopped or configured) state | |
| 150 // should send a start notification. | |
| 151 if (state_ == STOPPED || state_ == CONFIGURED) | |
| 152 NotifyStart(); | |
| 153 | |
| 154 // Stop requested data types. | |
| 155 for (size_t i = 0; i < needs_stop_.size(); ++i) { | |
| 156 VLOG(1) << "Stopping " << needs_stop_[i]->name(); | |
| 157 needs_stop_[i]->Stop(); | |
| 158 } | |
| 159 needs_stop_.clear(); | |
| 160 | |
| 161 // Tell the backend about the new set of data types we wish to sync. | |
| 162 // The task will be invoked when updates are downloaded. | |
| 163 state_ = DOWNLOAD_PENDING; | |
| 164 backend_->ConfigureDataTypes( | |
| 165 controllers_, | |
| 166 last_requested_types_, | |
| 167 method_factory_.NewRunnableMethod(&DataTypeManagerImpl2::DownloadReady)); | |
| 168 } | |
| 169 | |
| 170 void DataTypeManagerImpl2::DownloadReady() { | |
| 171 DCHECK(state_ == DOWNLOAD_PENDING || state_ == RESTARTING); | |
| 172 | |
| 173 // If we had a restart while waiting for downloads, just restart. | |
| 174 // Note: Restart() can cause DownloadReady to be directly invoked, so we post | |
| 175 // a task to avoid re-entrancy issues. | |
| 176 if (state_ == RESTARTING) { | |
| 177 MessageLoop::current()->PostTask(FROM_HERE, | |
| 178 method_factory_.NewRunnableMethod(&DataTypeManagerImpl2::Restart)); | |
| 179 return; | |
| 180 } | |
| 181 | |
| 182 state_ = CONFIGURING; | |
| 183 StartNextType(); | |
| 184 } | |
| 185 | |
| 186 void DataTypeManagerImpl2::StartNextType() { | |
| 187 // If there are any data types left to start, start the one at the | |
| 188 // front of the list. | |
| 189 if (needs_start_.size() > 0) { | |
| 190 current_dtc_ = needs_start_[0]; | |
| 191 VLOG(1) << "Starting " << current_dtc_->name(); | |
| 192 current_dtc_->Start( | |
| 193 NewCallback(this, &DataTypeManagerImpl2::TypeStartCallback)); | |
| 194 return; | |
| 195 } | |
| 196 | |
| 197 // If no more data types need starting, we're done. Resume the sync | |
|
Nicolas Zea
2011/02/16 18:40:53
"Resume sync backend" no longer applicable, right?
tim (not reviewing)
2011/02/17 21:27:25
Done.
| |
| 198 // backend to finish. | |
| 199 DCHECK_EQ(state_, CONFIGURING); | |
| 200 state_ = CONFIGURED; | |
| 201 NotifyDone(OK); | |
| 202 } | |
| 203 | |
| 204 void DataTypeManagerImpl2::TypeStartCallback( | |
| 205 DataTypeController::StartResult result) { | |
| 206 // When the data type controller invokes this callback, it must be | |
| 207 // on the UI thread. | |
| 208 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 209 DCHECK(current_dtc_); | |
| 210 | |
| 211 if (state_ == RESTARTING) { | |
| 212 // If configuration changed while this data type was starting, we | |
| 213 // need to reset. Resume the syncer. | |
|
Nicolas Zea
2011/02/16 18:40:53
Same here.
tim (not reviewing)
2011/02/17 21:27:25
Done.
| |
| 214 Restart(); | |
| 215 return; | |
| 216 } else if (state_ == STOPPING) { | |
| 217 // If we reach this callback while stopping, this means that | |
| 218 // DataTypeManager::Stop() was called while the current data type | |
| 219 // was starting. Now that it has finished starting, we can finish | |
| 220 // stopping the DataTypeManager. This is considered an ABORT. | |
| 221 FinishStopAndNotify(ABORTED); | |
| 222 return; | |
| 223 } else if (state_ == STOPPED) { | |
| 224 // If our state_ is STOPPED, we have already stopped all of the data | |
| 225 // types. We should not be getting callbacks from stopped data types. | |
| 226 LOG(ERROR) << "Start callback called by stopped data type!"; | |
| 227 return; | |
| 228 } | |
| 229 | |
| 230 // We're done with the data type at the head of the list -- remove it. | |
| 231 DataTypeController* started_dtc = current_dtc_; | |
| 232 DCHECK(needs_start_.size()); | |
| 233 DCHECK_EQ(needs_start_[0], started_dtc); | |
| 234 needs_start_.erase(needs_start_.begin()); | |
| 235 current_dtc_ = NULL; | |
| 236 | |
| 237 // If the type started normally, continue to the next type. | |
| 238 // If the type is waiting for the cryptographer, continue to the next type. | |
| 239 // Once the cryptographer is ready, we'll attempt to restart this type. | |
| 240 if (result == DataTypeController::NEEDS_CRYPTO || | |
| 241 result == DataTypeController::OK || | |
| 242 result == DataTypeController::OK_FIRST_RUN) { | |
| 243 StartNextType(); | |
| 244 return; | |
| 245 } | |
| 246 | |
| 247 // Any other result is a fatal error. Shut down any types we've | |
| 248 // managed to start up to this point and pass the result to the | |
| 249 // callback. | |
| 250 VLOG(1) << "Failed " << started_dtc->name(); | |
| 251 ConfigureResult configure_result = DataTypeManager::ABORTED; | |
| 252 switch (result) { | |
| 253 case DataTypeController::ABORTED: | |
| 254 configure_result = DataTypeManager::ABORTED; | |
| 255 break; | |
| 256 case DataTypeController::ASSOCIATION_FAILED: | |
| 257 configure_result = DataTypeManager::ASSOCIATION_FAILED; | |
| 258 break; | |
| 259 case DataTypeController::UNRECOVERABLE_ERROR: | |
| 260 configure_result = DataTypeManager::UNRECOVERABLE_ERROR; | |
| 261 break; | |
| 262 default: | |
| 263 NOTREACHED(); | |
| 264 break; | |
| 265 } | |
| 266 FinishStopAndNotify(configure_result); | |
| 267 } | |
| 268 | |
| 269 void DataTypeManagerImpl2::Stop() { | |
| 270 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 271 if (state_ == STOPPED) | |
| 272 return; | |
| 273 | |
| 274 // If we are currently configuring, then the current type is in a | |
| 275 // partially started state. Abort the startup of the current type, | |
| 276 // which will synchronously invoke the start callback. | |
| 277 if (state_ == CONFIGURING) { | |
| 278 state_ = STOPPING; | |
| 279 current_dtc_->Stop(); | |
| 280 return; | |
| 281 } | |
| 282 | |
| 283 const bool download_pending = state_ == DOWNLOAD_PENDING; | |
| 284 state_ = STOPPING; | |
| 285 if (download_pending) { | |
| 286 // If Stop() is called while waiting for download, cancel all | |
| 287 // outstanding tasks. | |
| 288 method_factory_.RevokeAll(); | |
| 289 FinishStopAndNotify(ABORTED); | |
| 290 return; | |
| 291 } | |
| 292 | |
| 293 FinishStop(); | |
| 294 } | |
| 295 | |
| 296 void DataTypeManagerImpl2::FinishStop() { | |
| 297 DCHECK(state_== CONFIGURING || state_ == STOPPING); | |
| 298 // Simply call the Stop() method on all running data types. | |
| 299 for (DataTypeController::TypeMap::const_iterator it = controllers_.begin(); | |
| 300 it != controllers_.end(); ++it) { | |
| 301 DataTypeController* dtc = (*it).second; | |
| 302 if (dtc->state() != DataTypeController::NOT_RUNNING && | |
| 303 dtc->state() != DataTypeController::STOPPING) { | |
| 304 dtc->Stop(); | |
| 305 VLOG(1) << "Stopped " << dtc->name(); | |
| 306 } | |
| 307 } | |
| 308 state_ = STOPPED; | |
| 309 } | |
| 310 | |
| 311 void DataTypeManagerImpl2::FinishStopAndNotify(ConfigureResult result) { | |
| 312 FinishStop(); | |
| 313 NotifyDone(result); | |
| 314 } | |
| 315 | |
| 316 void DataTypeManagerImpl2::NotifyStart() { | |
| 317 NotificationService::current()->Notify( | |
| 318 NotificationType::SYNC_CONFIGURE_START, | |
| 319 Source<DataTypeManager>(this), | |
| 320 NotificationService::NoDetails()); | |
| 321 } | |
| 322 | |
| 323 void DataTypeManagerImpl2::NotifyDone(ConfigureResult result) { | |
| 324 NotificationService::current()->Notify( | |
| 325 NotificationType::SYNC_CONFIGURE_DONE, | |
| 326 Source<DataTypeManager>(this), | |
| 327 Details<ConfigureResult>(&result)); | |
| 328 } | |
| 329 | |
| 330 const DataTypeController::TypeMap& DataTypeManagerImpl2::controllers() { | |
| 331 return controllers_; | |
| 332 } | |
| 333 | |
| 334 DataTypeManager::State DataTypeManagerImpl2::state() { | |
| 335 return state_; | |
| 336 } | |
| 337 | |
| 338 } // namespace browser_sync | |
| OLD | NEW |