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/non_frontend_data_type_controller.h" |
| 6 |
| 7 #include "base/logging.h" |
| 8 #include "chrome/browser/profiles/profile.h" |
| 9 #include "chrome/browser/sync/glue/change_processor.h" |
| 10 #include "chrome/browser/sync/glue/model_associator.h" |
| 11 #include "chrome/browser/sync/profile_sync_factory.h" |
| 12 #include "chrome/browser/sync/profile_sync_service.h" |
| 13 #include "chrome/browser/sync/syncable/model_type.h" |
| 14 #include "content/browser/browser_thread.h" |
| 15 |
| 16 namespace browser_sync { |
| 17 |
| 18 NonFrontendDataTypeController::NonFrontendDataTypeController() |
| 19 : profile_sync_factory_(NULL), |
| 20 profile_(NULL), |
| 21 profile_sync_service_(NULL), |
| 22 abort_association_(false), |
| 23 abort_association_complete_(false, false), |
| 24 datatype_stopped_(false, false) {} |
| 25 |
| 26 NonFrontendDataTypeController::NonFrontendDataTypeController( |
| 27 ProfileSyncFactory* profile_sync_factory, |
| 28 Profile* profile) |
| 29 : profile_sync_factory_(profile_sync_factory), |
| 30 profile_(profile), |
| 31 profile_sync_service_(profile->GetProfileSyncService()), |
| 32 state_(NOT_RUNNING), |
| 33 abort_association_(false), |
| 34 abort_association_complete_(false, false), |
| 35 datatype_stopped_(false, false) { |
| 36 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 37 DCHECK(profile_sync_factory_); |
| 38 DCHECK(profile_); |
| 39 DCHECK(profile_sync_service_); |
| 40 } |
| 41 |
| 42 NonFrontendDataTypeController::~NonFrontendDataTypeController() { |
| 43 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 44 } |
| 45 |
| 46 void NonFrontendDataTypeController::Start(StartCallback* start_callback) { |
| 47 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 48 DCHECK(start_callback); |
| 49 if (state_ != NOT_RUNNING) { |
| 50 start_callback->Run(BUSY, FROM_HERE); |
| 51 delete start_callback; |
| 52 return; |
| 53 } |
| 54 |
| 55 start_callback_.reset(start_callback); |
| 56 abort_association_ = false; |
| 57 |
| 58 state_ = MODEL_STARTING; |
| 59 if (!StartModels()) { |
| 60 // If we are waiting for some external service to load before associating |
| 61 // or we failed to start the models, we exit early. state_ will control |
| 62 // what we perform next. |
| 63 DCHECK(state_ == NOT_RUNNING || state_ == MODEL_STARTING); |
| 64 return; |
| 65 } |
| 66 |
| 67 // Kick off association on the thread the datatype resides on. |
| 68 state_ = ASSOCIATING; |
| 69 if (!StartAssociationAsync()) { |
| 70 StartDoneImpl(ASSOCIATION_FAILED, NOT_RUNNING, FROM_HERE); |
| 71 } |
| 72 } |
| 73 |
| 74 bool NonFrontendDataTypeController::StartModels() { |
| 75 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 76 DCHECK_EQ(state_, MODEL_STARTING); |
| 77 // By default, no additional services need to be started before we can proceed |
| 78 // with model association, so do nothing. |
| 79 return true; |
| 80 } |
| 81 |
| 82 void NonFrontendDataTypeController::StartAssociation() { |
| 83 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 84 DCHECK_EQ(state_, ASSOCIATING); |
| 85 { |
| 86 base::AutoLock lock(abort_association_lock_); |
| 87 if (abort_association_) { |
| 88 abort_association_complete_.Signal(); |
| 89 return; |
| 90 } |
| 91 CreateSyncComponents(); |
| 92 } |
| 93 |
| 94 if (!model_associator_->CryptoReadyIfNecessary()) { |
| 95 StartFailed(NEEDS_CRYPTO, FROM_HERE); |
| 96 return; |
| 97 } |
| 98 |
| 99 bool sync_has_nodes = false; |
| 100 if (!model_associator_->SyncModelHasUserCreatedNodes(&sync_has_nodes)) { |
| 101 StartFailed(UNRECOVERABLE_ERROR, FROM_HERE); |
| 102 return; |
| 103 } |
| 104 |
| 105 base::TimeTicks start_time = base::TimeTicks::Now(); |
| 106 bool merge_success = model_associator_->AssociateModels(); |
| 107 RecordAssociationTime(base::TimeTicks::Now() - start_time); |
| 108 if (!merge_success) { |
| 109 StartFailed(ASSOCIATION_FAILED, FROM_HERE); |
| 110 return; |
| 111 } |
| 112 |
| 113 profile_sync_service_->ActivateDataType(this, change_processor_.get()); |
| 114 StartDone(!sync_has_nodes ? OK_FIRST_RUN : OK, RUNNING, FROM_HERE); |
| 115 } |
| 116 |
| 117 void NonFrontendDataTypeController::StartFailed(StartResult result, |
| 118 const tracked_objects::Location& location) { |
| 119 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 120 model_associator_.reset(); |
| 121 change_processor_.reset(); |
| 122 StartDone(result, NOT_RUNNING, location); |
| 123 } |
| 124 |
| 125 void NonFrontendDataTypeController::StartDone( |
| 126 DataTypeController::StartResult result, |
| 127 DataTypeController::State new_state, |
| 128 const tracked_objects::Location& location) { |
| 129 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 130 abort_association_complete_.Signal(); |
| 131 base::AutoLock lock(abort_association_lock_); |
| 132 if (!abort_association_) { |
| 133 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
| 134 NewRunnableMethod( |
| 135 this, |
| 136 &NonFrontendDataTypeController::StartDoneImpl, |
| 137 result, |
| 138 new_state, |
| 139 location)); |
| 140 } |
| 141 } |
| 142 |
| 143 void NonFrontendDataTypeController::StartDoneImpl( |
| 144 DataTypeController::StartResult result, |
| 145 DataTypeController::State new_state, |
| 146 const tracked_objects::Location& location) { |
| 147 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 148 state_ = new_state; |
| 149 if (state_ != RUNNING) { |
| 150 // Start failed. |
| 151 StopModels(); |
| 152 RecordStartFailure(result); |
| 153 } |
| 154 |
| 155 // We have to release the callback before we call it, since it's possible |
| 156 // invoking the callback will trigger a call to STOP(), which will get |
| 157 // confused by the non-NULL start_callback_. |
| 158 scoped_ptr<StartCallback> callback(start_callback_.release()); |
| 159 callback->Run(result, location); |
| 160 } |
| 161 |
| 162 // TODO(sync): Blocking the UI thread at shutdown is bad. If we had a way of |
| 163 // distinguishing chrome shutdown from sync shutdown, we should be able to avoid |
| 164 // this (http://crbug.com/55662). |
| 165 void NonFrontendDataTypeController::Stop() { |
| 166 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 167 // If Stop() is called while Start() is waiting for association to |
| 168 // complete, we need to abort the association and wait for the DB |
| 169 // thread to finish the StartImpl() task. |
| 170 if (state_ == ASSOCIATING) { |
| 171 state_ = STOPPING; |
| 172 { |
| 173 base::AutoLock lock(abort_association_lock_); |
| 174 abort_association_ = true; |
| 175 if (model_associator_.get()) |
| 176 model_associator_->AbortAssociation(); |
| 177 } |
| 178 // Wait for the model association to abort. |
| 179 abort_association_complete_.Wait(); |
| 180 StartDoneImpl(ABORTED, STOPPING, FROM_HERE); |
| 181 } else if (state_ == MODEL_STARTING) { |
| 182 state_ = STOPPING; |
| 183 // If Stop() is called while Start() is waiting for the models to start, |
| 184 // abort the start. We don't need to continue on since it means we haven't |
| 185 // kicked off the association, and once we call StopModels, we never will. |
| 186 StartDoneImpl(ABORTED, NOT_RUNNING, FROM_HERE); |
| 187 return; |
| 188 } else { |
| 189 state_ = STOPPING; |
| 190 |
| 191 StopModels(); |
| 192 } |
| 193 DCHECK(!start_callback_.get()); |
| 194 |
| 195 // Deactivate the change processor on the UI thread. We dont want to listen |
| 196 // for any more changes or process them from server. |
| 197 if (change_processor_ != NULL) |
| 198 profile_sync_service_->DeactivateDataType(this, change_processor_.get()); |
| 199 |
| 200 if (StopAssociationAsync()) { |
| 201 datatype_stopped_.Wait(); |
| 202 } else { |
| 203 // We do DFATAL here because this will eventually lead to a failed CHECK |
| 204 // when the change processor gets destroyed on the wrong thread. |
| 205 LOG(DFATAL) << "Failed to destroy datatype " << name(); |
| 206 } |
| 207 state_ = NOT_RUNNING; |
| 208 } |
| 209 |
| 210 void NonFrontendDataTypeController::StopModels() { |
| 211 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 212 DCHECK(state_ == STOPPING || state_ == NOT_RUNNING); |
| 213 // Do nothing by default. |
| 214 } |
| 215 |
| 216 void NonFrontendDataTypeController::StopAssociation() { |
| 217 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 218 if (model_associator_ != NULL) |
| 219 model_associator_->DisassociateModels(); |
| 220 change_processor_.reset(); |
| 221 model_associator_.reset(); |
| 222 datatype_stopped_.Signal(); |
| 223 } |
| 224 |
| 225 std::string NonFrontendDataTypeController::name() const { |
| 226 // For logging only. |
| 227 return syncable::ModelTypeToString(type()); |
| 228 } |
| 229 |
| 230 DataTypeController::State NonFrontendDataTypeController::state() const { |
| 231 return state_; |
| 232 } |
| 233 |
| 234 void NonFrontendDataTypeController::OnUnrecoverableError( |
| 235 const tracked_objects::Location& from_here, |
| 236 const std::string& message) { |
| 237 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 238 RecordUnrecoverableError(from_here, message); |
| 239 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, NewRunnableMethod(this, |
| 240 &NonFrontendDataTypeController::OnUnrecoverableErrorImpl, from_here, |
| 241 message)); |
| 242 } |
| 243 |
| 244 void NonFrontendDataTypeController::OnUnrecoverableErrorImpl( |
| 245 const tracked_objects::Location& from_here, |
| 246 const std::string& message) { |
| 247 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 248 profile_sync_service_->OnUnrecoverableError(from_here, message); |
| 249 } |
| 250 |
| 251 ProfileSyncFactory* NonFrontendDataTypeController::profile_sync_factory() |
| 252 const { |
| 253 return profile_sync_factory_; |
| 254 } |
| 255 |
| 256 Profile* NonFrontendDataTypeController::profile() const { |
| 257 return profile_; |
| 258 } |
| 259 |
| 260 ProfileSyncService* NonFrontendDataTypeController::profile_sync_service() |
| 261 const { |
| 262 return profile_sync_service_; |
| 263 } |
| 264 |
| 265 void NonFrontendDataTypeController::set_state(State state) { |
| 266 state_ = state; |
| 267 } |
| 268 |
| 269 void NonFrontendDataTypeController::set_model_associator( |
| 270 AssociatorInterface* associator) { |
| 271 model_associator_.reset(associator); |
| 272 } |
| 273 |
| 274 void NonFrontendDataTypeController::set_change_processor( |
| 275 ChangeProcessor* change_processor) { |
| 276 change_processor_.reset(change_processor); |
| 277 } |
| 278 |
| 279 } // namespace browser_sync |
OLD | NEW |