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