| OLD | NEW | 
 | (Empty) | 
|    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 <memory> |  | 
|    8  |  | 
|    9 #include "base/bind.h" |  | 
|   10 #include "base/json/json_writer.h" |  | 
|   11 #include "base/threading/thread_task_runner_handle.h" |  | 
|   12 #include "base/values.h" |  | 
|   13  |  | 
|   14 namespace syncer { |  | 
|   15  |  | 
|   16 std::unique_ptr<base::DictionaryValue> ModelSafeRoutingInfoToValue( |  | 
|   17     const ModelSafeRoutingInfo& routing_info) { |  | 
|   18   std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue()); |  | 
|   19   for (ModelSafeRoutingInfo::const_iterator it = routing_info.begin(); |  | 
|   20        it != routing_info.end(); ++it) { |  | 
|   21     dict->SetString(ModelTypeToString(it->first), |  | 
|   22                     ModelSafeGroupToString(it->second)); |  | 
|   23   } |  | 
|   24   return dict; |  | 
|   25 } |  | 
|   26  |  | 
|   27 std::string ModelSafeRoutingInfoToString( |  | 
|   28     const ModelSafeRoutingInfo& routing_info) { |  | 
|   29   std::string json; |  | 
|   30   base::JSONWriter::Write(*ModelSafeRoutingInfoToValue(routing_info), &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     case GROUP_NON_BLOCKING: |  | 
|   69       return "GROUP_NON_BLOCKING"; |  | 
|   70     default: |  | 
|   71       NOTREACHED(); |  | 
|   72       return "INVALID"; |  | 
|   73   } |  | 
|   74 } |  | 
|   75  |  | 
|   76 ModelSafeWorker::ModelSafeWorker(WorkerLoopDestructionObserver* observer) |  | 
|   77     : stopped_(false), |  | 
|   78       work_done_or_stopped_(base::WaitableEvent::ResetPolicy::AUTOMATIC, |  | 
|   79                             base::WaitableEvent::InitialState::NOT_SIGNALED), |  | 
|   80       observer_(observer) {} |  | 
|   81  |  | 
|   82 ModelSafeWorker::~ModelSafeWorker() {} |  | 
|   83  |  | 
|   84 void ModelSafeWorker::RequestStop() { |  | 
|   85   base::AutoLock al(stopped_lock_); |  | 
|   86  |  | 
|   87   // Set stop flag but don't signal work_done_or_stopped_ to unblock sync loop |  | 
|   88   // because the worker may be working and depending on sync command object |  | 
|   89   // living on sync thread. This prevents any *further* tasks from being posted |  | 
|   90   // to worker threads (see DoWorkAndWaitUntilDone below), but note that one |  | 
|   91   // may already be posted. |  | 
|   92   stopped_ = true; |  | 
|   93 } |  | 
|   94  |  | 
|   95 SyncerError ModelSafeWorker::DoWorkAndWaitUntilDone(const WorkCallback& work) { |  | 
|   96   { |  | 
|   97     base::AutoLock al(stopped_lock_); |  | 
|   98     if (stopped_) |  | 
|   99       return CANNOT_DO_WORK; |  | 
|  100  |  | 
|  101     CHECK(!work_done_or_stopped_.IsSignaled()); |  | 
|  102   } |  | 
|  103  |  | 
|  104   return DoWorkAndWaitUntilDoneImpl(work); |  | 
|  105 } |  | 
|  106  |  | 
|  107 bool ModelSafeWorker::IsStopped() { |  | 
|  108   base::AutoLock al(stopped_lock_); |  | 
|  109   return stopped_; |  | 
|  110 } |  | 
|  111  |  | 
|  112 void ModelSafeWorker::WillDestroyCurrentMessageLoop() { |  | 
|  113   { |  | 
|  114     base::AutoLock al(stopped_lock_); |  | 
|  115     stopped_ = true; |  | 
|  116  |  | 
|  117     // Must signal to unblock syncer if it's waiting for a posted task to |  | 
|  118     // finish. At this point, all pending tasks posted to the loop have been |  | 
|  119     // destroyed (see MessageLoop::~MessageLoop). So syncer will be blocked |  | 
|  120     // indefinitely without signaling here. |  | 
|  121     work_done_or_stopped_.Signal(); |  | 
|  122  |  | 
|  123     DVLOG(1) << ModelSafeGroupToString(GetModelSafeGroup()) |  | 
|  124         << " worker stops on destruction of its working thread."; |  | 
|  125   } |  | 
|  126  |  | 
|  127   { |  | 
|  128     base::AutoLock l(working_task_runner_lock_); |  | 
|  129     working_task_runner_ = NULL; |  | 
|  130   } |  | 
|  131  |  | 
|  132   if (observer_) |  | 
|  133     observer_->OnWorkerLoopDestroyed(GetModelSafeGroup()); |  | 
|  134 } |  | 
|  135  |  | 
|  136 void ModelSafeWorker::SetWorkingLoopToCurrent() { |  | 
|  137   base::Callback<void(ModelSafeGroup)> unregister_done_callback; |  | 
|  138  |  | 
|  139   { |  | 
|  140     base::AutoLock l(working_task_runner_lock_); |  | 
|  141     DCHECK(!working_task_runner_); |  | 
|  142  |  | 
|  143     if (unregister_done_callback_.is_null()) { |  | 
|  144       // Expected case - UnregisterForLoopDestruction hasn't been called yet. |  | 
|  145       base::MessageLoop::current()->AddDestructionObserver(this); |  | 
|  146       working_task_runner_ = base::ThreadTaskRunnerHandle::Get(); |  | 
|  147     } else { |  | 
|  148       // Rare case which is possible when the model type thread remains |  | 
|  149       // blocked for the entire session and UnregisterForLoopDestruction ends |  | 
|  150       // up being called before this method. This method is posted unlike |  | 
|  151       // UnregisterForLoopDestruction - that's why they can end up being called |  | 
|  152       // out of order. |  | 
|  153       // In this case we skip the destruction observer registration |  | 
|  154       // and just invoke the callback stored at UnregisterForLoopDestruction. |  | 
|  155       DCHECK(stopped_); |  | 
|  156       unregister_done_callback = unregister_done_callback_; |  | 
|  157       unregister_done_callback_.Reset(); |  | 
|  158     } |  | 
|  159   } |  | 
|  160  |  | 
|  161   if (!unregister_done_callback.is_null()) { |  | 
|  162     unregister_done_callback.Run(GetModelSafeGroup()); |  | 
|  163   } |  | 
|  164 } |  | 
|  165  |  | 
|  166 void ModelSafeWorker::UnregisterForLoopDestruction( |  | 
|  167     base::Callback<void(ModelSafeGroup)> unregister_done_callback) { |  | 
|  168   base::AutoLock l(working_task_runner_lock_); |  | 
|  169   if (working_task_runner_) { |  | 
|  170     // Normal case - observer registration has been already done. |  | 
|  171     // Delegate to the sync thread to do the actual unregistration in |  | 
|  172     // UnregisterForLoopDestructionAsync. |  | 
|  173     DCHECK(!working_task_runner_->BelongsToCurrentThread()); |  | 
|  174     working_task_runner_->PostTask( |  | 
|  175         FROM_HERE, |  | 
|  176         base::Bind(&ModelSafeWorker::UnregisterForLoopDestructionAsync, 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_task_runner_lock_); |  | 
|  190     if (!working_task_runner_) |  | 
|  191       return; |  | 
|  192     DCHECK(working_task_runner_->BelongsToCurrentThread()); |  | 
|  193   } |  | 
|  194  |  | 
|  195   DCHECK(stopped_); |  | 
|  196   base::MessageLoop::current()->RemoveDestructionObserver(this); |  | 
|  197   unregister_done_callback.Run(GetModelSafeGroup()); |  | 
|  198 } |  | 
|  199  |  | 
|  200 }  // namespace syncer |  | 
| OLD | NEW |