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 ModelTypeInvalidationMap ModelSafeRoutingInfoToInvalidationMap( 35 const ModelSafeRoutingInfo& routes, 36 const std::string& payload) { 37 ModelTypeInvalidationMap invalidation_map; 38 for (ModelSafeRoutingInfo::const_iterator i = routes.begin(); 39 i != routes.end(); ++i) { 40 invalidation_map[i->first].payload = payload; 41 } 42 return invalidation_map; 43 } 44 45 ModelTypeSet GetRoutingInfoTypes(const ModelSafeRoutingInfo& routing_info) { 46 ModelTypeSet types; 47 for (ModelSafeRoutingInfo::const_iterator it = routing_info.begin(); 48 it != routing_info.end(); ++it) { 49 types.Put(it->first); 50 } 51 return types; 52 } 53 54 ModelSafeGroup GetGroupForModelType(const ModelType type, 55 const ModelSafeRoutingInfo& routes) { 56 ModelSafeRoutingInfo::const_iterator it = routes.find(type); 57 if (it == routes.end()) { 58 if (type != UNSPECIFIED && type != TOP_LEVEL_FOLDER) 59 DVLOG(1) << "Entry does not belong to active ModelSafeGroup!"; 60 return GROUP_PASSIVE; 61 } 62 return it->second; 63 } 64 65 std::string ModelSafeGroupToString(ModelSafeGroup group) { 66 switch (group) { 67 case GROUP_UI: 68 return "GROUP_UI"; 69 case GROUP_DB: 70 return "GROUP_DB"; 71 case GROUP_FILE: 72 return "GROUP_FILE"; 73 case GROUP_HISTORY: 74 return "GROUP_HISTORY"; 75 case GROUP_PASSIVE: 76 return "GROUP_PASSIVE"; 77 case GROUP_PASSWORD: 78 return "GROUP_PASSWORD"; 79 default: 80 NOTREACHED(); 81 return "INVALID"; 82 } 83 } 84 85 ModelSafeWorker::ModelSafeWorker(WorkerLoopDestructionObserver* observer) 86 : stopped_(false), 87 work_done_or_stopped_(false, false), 88 observer_(observer), 89 working_loop_(NULL), 90 working_loop_set_wait_(true, false) {} 91 92 ModelSafeWorker::~ModelSafeWorker() {} 93 94 void ModelSafeWorker::RequestStop() { 95 base::AutoLock al(stopped_lock_); 96 97 // Set stop flag but don't signal work_done_or_stopped_ to unblock sync loop 98 // because the worker may be working and depending on sync command object 99 // living on sync thread. his prevents any *further* tasks from being posted 100 // to worker threads (see DoWorkAndWaitUntilDone below), but note that one 101 // may already be posted. 102 stopped_ = true; 103 } 104 105 SyncerError ModelSafeWorker::DoWorkAndWaitUntilDone(const WorkCallback& work) { 106 { 107 base::AutoLock al(stopped_lock_); 108 if (stopped_) 109 return CANNOT_DO_WORK; 110 111 CHECK(!work_done_or_stopped_.IsSignaled()); 112 } 113 114 return DoWorkAndWaitUntilDoneImpl(work); 115 } 116 117 bool ModelSafeWorker::IsStopped() { 118 base::AutoLock al(stopped_lock_); 119 return stopped_; 120 } 121 122 void ModelSafeWorker::WillDestroyCurrentMessageLoop() { 123 { 124 base::AutoLock al(stopped_lock_); 125 stopped_ = true; 126 127 // Must signal to unblock syncer if it's waiting for a posted task to 128 // finish. At this point, all pending tasks posted to the loop have been 129 // destroyed (see MessageLoop::~MessageLoop). So syncer will be blocked 130 // indefinitely without signaling here. 131 work_done_or_stopped_.Signal(); 132 133 DVLOG(1) << ModelSafeGroupToString(GetModelSafeGroup()) 134 << " worker stops on destruction of its working thread."; 135 } 136 137 { 138 base::AutoLock l(working_loop_lock_); 139 working_loop_ = NULL; 140 } 141 142 if (observer_) 143 observer_->OnWorkerLoopDestroyed(GetModelSafeGroup()); 144 } 145 146 void ModelSafeWorker::SetWorkingLoopToCurrent() { 147 base::AutoLock l(working_loop_lock_); 148 DCHECK(!working_loop_); 149 working_loop_ = base::MessageLoop::current(); 150 working_loop_set_wait_.Signal(); 151 } 152 153 void ModelSafeWorker::UnregisterForLoopDestruction( 154 base::Callback<void(ModelSafeGroup)> unregister_done_callback) { 155 // Ok to wait until |working_loop_| is set because this is called on sync 156 // loop. 157 working_loop_set_wait_.Wait(); 158 159 { 160 base::AutoLock l(working_loop_lock_); 161 if (working_loop_ != NULL) { 162 // Should be called on sync loop. 163 DCHECK_NE(base::MessageLoop::current(), working_loop_); 164 working_loop_->PostTask( 165 FROM_HERE, 166 base::Bind(&ModelSafeWorker::UnregisterForLoopDestructionAsync, 167 this, unregister_done_callback)); 168 } 169 } 170 } 171 172 void ModelSafeWorker::UnregisterForLoopDestructionAsync( 173 base::Callback<void(ModelSafeGroup)> unregister_done_callback) { 174 { 175 base::AutoLock l(working_loop_lock_); 176 if (!working_loop_) 177 return; 178 DCHECK_EQ(base::MessageLoop::current(), working_loop_); 179 } 180 181 DCHECK(stopped_); 182 base::MessageLoop::current()->RemoveDestructionObserver(this); 183 unregister_done_callback.Run(GetModelSafeGroup()); 184 } 185 186 } // namespace syncer 187