Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / ppapi / proxy / ppb_message_loop_proxy.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 "ppapi/proxy/ppb_message_loop_proxy.h"
6
7 #include <vector>
8
9 #include "base/bind.h"
10 #include "base/compiler_specific.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/message_loop/message_loop_proxy.h"
13 #include "ppapi/c/pp_errors.h"
14 #include "ppapi/c/ppb_message_loop.h"
15 #include "ppapi/proxy/plugin_dispatcher.h"
16 #include "ppapi/proxy/plugin_globals.h"
17 #include "ppapi/shared_impl/proxy_lock.h"
18 #include "ppapi/thunk/enter.h"
19
20 using ppapi::thunk::PPB_MessageLoop_API;
21
22 namespace ppapi {
23 namespace proxy {
24
25 namespace {
26 typedef thunk::EnterResource<PPB_MessageLoop_API> EnterMessageLoop;
27 }
28
29 MessageLoopResource::MessageLoopResource(PP_Instance instance)
30     : MessageLoopShared(instance),
31       nested_invocations_(0),
32       destroyed_(false),
33       should_destroy_(false),
34       is_main_thread_loop_(false),
35       currently_handling_blocking_message_(false) {
36 }
37
38 MessageLoopResource::MessageLoopResource(ForMainThread for_main_thread)
39     : MessageLoopShared(for_main_thread),
40       nested_invocations_(0),
41       destroyed_(false),
42       should_destroy_(false),
43       is_main_thread_loop_(true),
44       currently_handling_blocking_message_(false) {
45   // We attach the main thread immediately. We can't use AttachToCurrentThread,
46   // because the MessageLoop already exists.
47
48   // This must be called only once, so the slot must be empty.
49   CHECK(!PluginGlobals::Get()->msg_loop_slot());
50   // We don't add a reference for TLS here, so we don't release it. Instead,
51   // this loop is owned by PluginGlobals. Contrast with AttachToCurrentThread
52   // where we register ReleaseMessageLoop with TLS and call AddRef.
53   base::ThreadLocalStorage::Slot* slot = new base::ThreadLocalStorage::Slot();
54   PluginGlobals::Get()->set_msg_loop_slot(slot);
55
56   slot->Set(this);
57
58   loop_proxy_ = base::MessageLoopProxy::current();
59 }
60
61
62 MessageLoopResource::~MessageLoopResource() {
63 }
64
65 PPB_MessageLoop_API* MessageLoopResource::AsPPB_MessageLoop_API() {
66   return this;
67 }
68
69 int32_t MessageLoopResource::AttachToCurrentThread() {
70   if (is_main_thread_loop_)
71     return PP_ERROR_INPROGRESS;
72
73   PluginGlobals* globals = PluginGlobals::Get();
74
75   base::ThreadLocalStorage::Slot* slot = globals->msg_loop_slot();
76   if (!slot) {
77     slot = new base::ThreadLocalStorage::Slot(&ReleaseMessageLoop);
78     globals->set_msg_loop_slot(slot);
79   } else {
80     if (slot->Get())
81       return PP_ERROR_INPROGRESS;
82   }
83   // TODO(dmichael) check that the current thread can support a message loop.
84
85   // Take a ref to the MessageLoop on behalf of the TLS. Note that this is an
86   // internal ref and not a plugin ref so the plugin can't accidentally
87   // release it. This is released by ReleaseMessageLoop().
88   AddRef();
89   slot->Set(this);
90
91   loop_.reset(new base::MessageLoop);
92   loop_proxy_ = base::MessageLoopProxy::current();
93
94   // Post all pending work to the message loop.
95   for (size_t i = 0; i < pending_tasks_.size(); i++) {
96     const TaskInfo& info = pending_tasks_[i];
97     PostClosure(info.from_here, info.closure, info.delay_ms);
98   }
99   pending_tasks_.clear();
100
101   return PP_OK;
102 }
103
104 int32_t MessageLoopResource::Run() {
105   if (!IsCurrent())
106     return PP_ERROR_WRONG_THREAD;
107   if (is_main_thread_loop_)
108     return PP_ERROR_INPROGRESS;
109
110   nested_invocations_++;
111   CallWhileUnlocked(
112       base::Bind(&base::MessageLoop::Run, base::Unretained(loop_.get())));
113   nested_invocations_--;
114
115   if (should_destroy_ && nested_invocations_ == 0) {
116     loop_proxy_ = NULL;
117     loop_.reset();
118     destroyed_ = true;
119   }
120   return PP_OK;
121 }
122
123 int32_t MessageLoopResource::PostWork(PP_CompletionCallback callback,
124                                       int64_t delay_ms) {
125   if (!callback.func)
126     return PP_ERROR_BADARGUMENT;
127   if (destroyed_)
128     return PP_ERROR_FAILED;
129   PostClosure(FROM_HERE,
130               base::Bind(callback.func, callback.user_data,
131                          static_cast<int32_t>(PP_OK)),
132               delay_ms);
133   return PP_OK;
134 }
135
136 int32_t MessageLoopResource::PostQuit(PP_Bool should_destroy) {
137   if (is_main_thread_loop_)
138     return PP_ERROR_WRONG_THREAD;
139
140   if (PP_ToBool(should_destroy))
141     should_destroy_ = true;
142
143   if (IsCurrent() && nested_invocations_ > 0)
144     loop_->Quit();
145   else
146     PostClosure(FROM_HERE, base::MessageLoop::QuitClosure(), 0);
147   return PP_OK;
148 }
149
150 // static
151 MessageLoopResource* MessageLoopResource::GetCurrent() {
152   PluginGlobals* globals = PluginGlobals::Get();
153   if (!globals->msg_loop_slot())
154     return NULL;
155   return reinterpret_cast<MessageLoopResource*>(
156       globals->msg_loop_slot()->Get());
157 }
158
159 void MessageLoopResource::DetachFromThread() {
160   // Note that the message loop must be destroyed on the thread it was created
161   // on.
162   loop_proxy_ = NULL;
163   loop_.reset();
164
165   // Cancel out the AddRef in AttachToCurrentThread().
166   Release();
167   // DANGER: may delete this.
168 }
169
170 bool MessageLoopResource::IsCurrent() const {
171   PluginGlobals* globals = PluginGlobals::Get();
172   if (!globals->msg_loop_slot())
173     return false;  // Can't be current if there's nothing in the slot.
174   return static_cast<const void*>(globals->msg_loop_slot()->Get()) ==
175          static_cast<const void*>(this);
176 }
177
178 void MessageLoopResource::PostClosure(
179     const tracked_objects::Location& from_here,
180     const base::Closure& closure,
181     int64 delay_ms) {
182   if (loop_proxy_.get()) {
183     loop_proxy_->PostDelayedTask(
184         from_here, closure, base::TimeDelta::FromMilliseconds(delay_ms));
185   } else {
186     TaskInfo info;
187     info.from_here = FROM_HERE;
188     info.closure = closure;
189     info.delay_ms = delay_ms;
190     pending_tasks_.push_back(info);
191   }
192 }
193
194 base::MessageLoopProxy* MessageLoopResource::GetMessageLoopProxy() {
195   return loop_proxy_.get();
196 }
197
198 bool MessageLoopResource::CurrentlyHandlingBlockingMessage() {
199   return currently_handling_blocking_message_;
200 }
201
202 // static
203 void MessageLoopResource::ReleaseMessageLoop(void* value) {
204   static_cast<MessageLoopResource*>(value)->DetachFromThread();
205 }
206
207 // -----------------------------------------------------------------------------
208
209 PP_Resource Create(PP_Instance instance) {
210   ProxyAutoLock lock;
211   // Validate the instance.
212   PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance);
213   if (!dispatcher)
214     return 0;
215   return (new MessageLoopResource(instance))->GetReference();
216 }
217
218 PP_Resource GetForMainThread() {
219   ProxyAutoLock lock;
220   return PluginGlobals::Get()->loop_for_main_thread()->GetReference();
221 }
222
223 PP_Resource GetCurrent() {
224   ProxyAutoLock lock;
225   Resource* resource = MessageLoopResource::GetCurrent();
226   if (resource)
227     return resource->GetReference();
228   return 0;
229 }
230
231 int32_t AttachToCurrentThread(PP_Resource message_loop) {
232   EnterMessageLoop enter(message_loop, true);
233   if (enter.succeeded())
234     return enter.object()->AttachToCurrentThread();
235   return PP_ERROR_BADRESOURCE;
236 }
237
238 int32_t Run(PP_Resource message_loop) {
239   EnterMessageLoop enter(message_loop, true);
240   if (enter.succeeded())
241     return enter.object()->Run();
242   return PP_ERROR_BADRESOURCE;
243 }
244
245 int32_t PostWork(PP_Resource message_loop,
246                  PP_CompletionCallback callback,
247                  int64_t delay_ms) {
248   EnterMessageLoop enter(message_loop, true);
249   if (enter.succeeded())
250     return enter.object()->PostWork(callback, delay_ms);
251   return PP_ERROR_BADRESOURCE;
252 }
253
254 int32_t PostQuit(PP_Resource message_loop, PP_Bool should_destroy) {
255   EnterMessageLoop enter(message_loop, true);
256   if (enter.succeeded())
257     return enter.object()->PostQuit(should_destroy);
258   return PP_ERROR_BADRESOURCE;
259 }
260
261 const PPB_MessageLoop_1_0 ppb_message_loop_interface = {
262   &Create,
263   &GetForMainThread,
264   &GetCurrent,
265   &AttachToCurrentThread,
266   &Run,
267   &PostWork,
268   &PostQuit
269 };
270
271 PPB_MessageLoop_Proxy::PPB_MessageLoop_Proxy(Dispatcher* dispatcher)
272     : InterfaceProxy(dispatcher) {
273 }
274
275 PPB_MessageLoop_Proxy::~PPB_MessageLoop_Proxy() {
276 }
277
278 // static
279 const PPB_MessageLoop_1_0* PPB_MessageLoop_Proxy::GetInterface() {
280   return &ppb_message_loop_interface;
281 }
282
283 }  // namespace proxy
284 }  // namespace ppapi