Home | History | Annotate | Download | only in engine
      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