Upstream version 11.40.277.0
[platform/framework/web/crosswalk.git] / src / sync / internal_api / public / engine / model_safe_worker.cc
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