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 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