// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "sync/internal_api/public/engine/model_safe_worker.h" #include "base/bind.h" #include "base/json/json_writer.h" #include "base/memory/scoped_ptr.h" #include "base/values.h" namespace syncer { base::DictionaryValue* ModelSafeRoutingInfoToValue( const ModelSafeRoutingInfo& routing_info) { base::DictionaryValue* dict = new base::DictionaryValue(); for (ModelSafeRoutingInfo::const_iterator it = routing_info.begin(); it != routing_info.end(); ++it) { dict->SetString(ModelTypeToString(it->first), ModelSafeGroupToString(it->second)); } return dict; } std::string ModelSafeRoutingInfoToString( const ModelSafeRoutingInfo& routing_info) { scoped_ptr dict( ModelSafeRoutingInfoToValue(routing_info)); std::string json; base::JSONWriter::Write(dict.get(), &json); return json; } ModelTypeSet GetRoutingInfoTypes(const ModelSafeRoutingInfo& routing_info) { ModelTypeSet types; for (ModelSafeRoutingInfo::const_iterator it = routing_info.begin(); it != routing_info.end(); ++it) { types.Put(it->first); } return types; } ModelSafeGroup GetGroupForModelType(const ModelType type, const ModelSafeRoutingInfo& routes) { ModelSafeRoutingInfo::const_iterator it = routes.find(type); if (it == routes.end()) { if (type != UNSPECIFIED && type != TOP_LEVEL_FOLDER) DVLOG(1) << "Entry does not belong to active ModelSafeGroup!"; return GROUP_PASSIVE; } return it->second; } std::string ModelSafeGroupToString(ModelSafeGroup group) { switch (group) { case GROUP_UI: return "GROUP_UI"; case GROUP_DB: return "GROUP_DB"; case GROUP_FILE: return "GROUP_FILE"; case GROUP_HISTORY: return "GROUP_HISTORY"; case GROUP_PASSIVE: return "GROUP_PASSIVE"; case GROUP_PASSWORD: return "GROUP_PASSWORD"; default: NOTREACHED(); return "INVALID"; } } ModelSafeWorker::ModelSafeWorker(WorkerLoopDestructionObserver* observer) : stopped_(false), work_done_or_stopped_(false, false), observer_(observer), working_loop_(NULL), working_loop_set_wait_(true, false) {} ModelSafeWorker::~ModelSafeWorker() {} void ModelSafeWorker::RequestStop() { base::AutoLock al(stopped_lock_); // Set stop flag but don't signal work_done_or_stopped_ to unblock sync loop // because the worker may be working and depending on sync command object // living on sync thread. his prevents any *further* tasks from being posted // to worker threads (see DoWorkAndWaitUntilDone below), but note that one // may already be posted. stopped_ = true; } SyncerError ModelSafeWorker::DoWorkAndWaitUntilDone(const WorkCallback& work) { { base::AutoLock al(stopped_lock_); if (stopped_) return CANNOT_DO_WORK; CHECK(!work_done_or_stopped_.IsSignaled()); } return DoWorkAndWaitUntilDoneImpl(work); } bool ModelSafeWorker::IsStopped() { base::AutoLock al(stopped_lock_); return stopped_; } void ModelSafeWorker::WillDestroyCurrentMessageLoop() { { base::AutoLock al(stopped_lock_); stopped_ = true; // Must signal to unblock syncer if it's waiting for a posted task to // finish. At this point, all pending tasks posted to the loop have been // destroyed (see MessageLoop::~MessageLoop). So syncer will be blocked // indefinitely without signaling here. work_done_or_stopped_.Signal(); DVLOG(1) << ModelSafeGroupToString(GetModelSafeGroup()) << " worker stops on destruction of its working thread."; } { base::AutoLock l(working_loop_lock_); working_loop_ = NULL; } if (observer_) observer_->OnWorkerLoopDestroyed(GetModelSafeGroup()); } void ModelSafeWorker::SetWorkingLoopToCurrent() { base::AutoLock l(working_loop_lock_); DCHECK(!working_loop_); working_loop_ = base::MessageLoop::current(); working_loop_set_wait_.Signal(); } void ModelSafeWorker::UnregisterForLoopDestruction( base::Callback unregister_done_callback) { // Ok to wait until |working_loop_| is set because this is called on sync // loop. working_loop_set_wait_.Wait(); { base::AutoLock l(working_loop_lock_); if (working_loop_ != NULL) { // Should be called on sync loop. DCHECK_NE(base::MessageLoop::current(), working_loop_); working_loop_->PostTask( FROM_HERE, base::Bind(&ModelSafeWorker::UnregisterForLoopDestructionAsync, this, unregister_done_callback)); } } } void ModelSafeWorker::UnregisterForLoopDestructionAsync( base::Callback unregister_done_callback) { { base::AutoLock l(working_loop_lock_); if (!working_loop_) return; DCHECK_EQ(base::MessageLoop::current(), working_loop_); } DCHECK(stopped_); base::MessageLoop::current()->RemoveDestructionObserver(this); unregister_done_callback.Run(GetModelSafeGroup()); } } // namespace syncer