1 // Copyright (c) 2012 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 "sync/internal_api/public/engine/model_safe_worker.h" 6 7 #include "base/bind.h" 8 #include "base/json/json_writer.h" 9 #include "base/memory/scoped_ptr.h" 10 #include "base/values.h" 11 12 namespace syncer { 13 14 base::DictionaryValue* ModelSafeRoutingInfoToValue( 15 const ModelSafeRoutingInfo& routing_info) { 16 base::DictionaryValue* dict = new base::DictionaryValue(); 17 for (ModelSafeRoutingInfo::const_iterator it = routing_info.begin(); 18 it != routing_info.end(); ++it) { 19 dict->SetString(ModelTypeToString(it->first), 20 ModelSafeGroupToString(it->second)); 21 } 22 return dict; 23 } 24 25 std::string ModelSafeRoutingInfoToString( 26 const ModelSafeRoutingInfo& routing_info) { 27 scoped_ptr<base::DictionaryValue> dict( 28 ModelSafeRoutingInfoToValue(routing_info)); 29 std::string json; 30 base::JSONWriter::Write(dict.get(), &json); 31 return json; 32 } 33 34 ModelTypeSet GetRoutingInfoTypes(const ModelSafeRoutingInfo& routing_info) { 35 ModelTypeSet types; 36 for (ModelSafeRoutingInfo::const_iterator it = routing_info.begin(); 37 it != routing_info.end(); ++it) { 38 types.Put(it->first); 39 } 40 return types; 41 } 42 43 ModelSafeGroup GetGroupForModelType(const ModelType type, 44 const ModelSafeRoutingInfo& routes) { 45 ModelSafeRoutingInfo::const_iterator it = routes.find(type); 46 if (it == routes.end()) { 47 if (type != UNSPECIFIED && type != TOP_LEVEL_FOLDER) 48 DVLOG(1) << "Entry does not belong to active ModelSafeGroup!"; 49 return GROUP_PASSIVE; 50 } 51 return it->second; 52 } 53 54 std::string ModelSafeGroupToString(ModelSafeGroup group) { 55 switch (group) { 56 case GROUP_UI: 57 return "GROUP_UI"; 58 case GROUP_DB: 59 return "GROUP_DB"; 60 case GROUP_FILE: 61 return "GROUP_FILE"; 62 case GROUP_HISTORY: 63 return "GROUP_HISTORY"; 64 case GROUP_PASSIVE: 65 return "GROUP_PASSIVE"; 66 case GROUP_PASSWORD: 67 return "GROUP_PASSWORD"; 68 default: 69 NOTREACHED(); 70 return "INVALID"; 71 } 72 } 73 74 ModelSafeWorker::ModelSafeWorker(WorkerLoopDestructionObserver* observer) 75 : stopped_(false), 76 work_done_or_stopped_(false, false), 77 observer_(observer), 78 working_loop_(NULL) { 79 } 80 81 ModelSafeWorker::~ModelSafeWorker() {} 82 83 void ModelSafeWorker::RequestStop() { 84 base::AutoLock al(stopped_lock_); 85 86 // Set stop flag but don't signal work_done_or_stopped_ to unblock sync loop 87 // because the worker may be working and depending on sync command object 88 // living on sync thread. his prevents any *further* tasks from being posted 89 // to worker threads (see DoWorkAndWaitUntilDone below), but note that one 90 // may already be posted. 91 stopped_ = true; 92 } 93 94 SyncerError ModelSafeWorker::DoWorkAndWaitUntilDone(const WorkCallback& work) { 95 { 96 base::AutoLock al(stopped_lock_); 97 if (stopped_) 98 return CANNOT_DO_WORK; 99 100 CHECK(!work_done_or_stopped_.IsSignaled()); 101 } 102 103 return DoWorkAndWaitUntilDoneImpl(work); 104 } 105 106 bool ModelSafeWorker::IsStopped() { 107 base::AutoLock al(stopped_lock_); 108 return stopped_; 109 } 110 111 void ModelSafeWorker::WillDestroyCurrentMessageLoop() { 112 { 113 base::AutoLock al(stopped_lock_); 114 stopped_ = true; 115 116 // Must signal to unblock syncer if it's waiting for a posted task to 117 // finish. At this point, all pending tasks posted to the loop have been 118 // destroyed (see MessageLoop::~MessageLoop). So syncer will be blocked 119 // indefinitely without signaling here. 120 work_done_or_stopped_.Signal(); 121 122 DVLOG(1) << ModelSafeGroupToString(GetModelSafeGroup()) 123 << " worker stops on destruction of its working thread."; 124 } 125 126 { 127 base::AutoLock l(working_loop_lock_); 128 working_loop_ = NULL; 129 } 130 131 if (observer_) 132 observer_->OnWorkerLoopDestroyed(GetModelSafeGroup()); 133 } 134 135 void ModelSafeWorker::SetWorkingLoopToCurrent() { 136 base::Callback<void(ModelSafeGroup)> unregister_done_callback; 137 138 { 139 base::AutoLock l(working_loop_lock_); 140 DCHECK(!working_loop_); 141 142 if (unregister_done_callback_.is_null()) { 143 // Expected case - UnregisterForLoopDestruction hasn't been called yet. 144 base::MessageLoop::current()->AddDestructionObserver(this); 145 working_loop_ = base::MessageLoop::current(); 146 } else { 147 // Rare case which is possible when the model type thread remains 148 // blocked for the entire session and UnregisterForLoopDestruction ends 149 // up being called before this method. This method is posted unlike 150 // UnregisterForLoopDestruction - that's why they can end up being called 151 // out of order. 152 // In this case we skip the destruction observer registration 153 // and just invoke the callback stored at UnregisterForLoopDestruction. 154 DCHECK(stopped_); 155 unregister_done_callback = unregister_done_callback_; 156 unregister_done_callback_.Reset(); 157 } 158 } 159 160 if (!unregister_done_callback.is_null()) { 161 unregister_done_callback.Run(GetModelSafeGroup()); 162 } 163 } 164 165 void ModelSafeWorker::UnregisterForLoopDestruction( 166 base::Callback<void(ModelSafeGroup)> unregister_done_callback) { 167 base::AutoLock l(working_loop_lock_); 168 if (working_loop_ != NULL) { 169 // Normal case - observer registration has been already done. 170 // Delegate to the sync thread to do the actual unregistration in 171 // UnregisterForLoopDestructionAsync. 172 DCHECK_NE(base::MessageLoop::current(), working_loop_); 173 working_loop_->PostTask( 174 FROM_HERE, 175 base::Bind(&ModelSafeWorker::UnregisterForLoopDestructionAsync, 176 this, 177 unregister_done_callback)); 178 } else { 179 // The working loop is still unknown, probably because the model type 180 // thread is blocked. Store the callback to be called from 181 // SetWorkingLoopToCurrent. 182 unregister_done_callback_ = unregister_done_callback; 183 } 184 } 185 186 void ModelSafeWorker::UnregisterForLoopDestructionAsync( 187 base::Callback<void(ModelSafeGroup)> unregister_done_callback) { 188 { 189 base::AutoLock l(working_loop_lock_); 190 if (!working_loop_) 191 return; 192 DCHECK_EQ(base::MessageLoop::current(), working_loop_); 193 } 194 195 DCHECK(stopped_); 196 base::MessageLoop::current()->RemoveDestructionObserver(this); 197 unregister_done_callback.Run(GetModelSafeGroup()); 198 } 199 200 } // namespace syncer 201