• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
MessageLoopResource(PP_Instance instance)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 
MessageLoopResource(ForMainThread for_main_thread)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 
~MessageLoopResource()62 MessageLoopResource::~MessageLoopResource() {
63 }
64 
AsPPB_MessageLoop_API()65 PPB_MessageLoop_API* MessageLoopResource::AsPPB_MessageLoop_API() {
66   return this;
67 }
68 
AttachToCurrentThread()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 
Run()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 
PostWork(PP_CompletionCallback callback,int64_t delay_ms)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 
PostQuit(PP_Bool should_destroy)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
GetCurrent()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 
DetachFromThread()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 
IsCurrent() const170 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 
PostClosure(const tracked_objects::Location & from_here,const base::Closure & closure,int64 delay_ms)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 
GetMessageLoopProxy()194 base::MessageLoopProxy* MessageLoopResource::GetMessageLoopProxy() {
195   return loop_proxy_.get();
196 }
197 
CurrentlyHandlingBlockingMessage()198 bool MessageLoopResource::CurrentlyHandlingBlockingMessage() {
199   return currently_handling_blocking_message_;
200 }
201 
202 // static
ReleaseMessageLoop(void * value)203 void MessageLoopResource::ReleaseMessageLoop(void* value) {
204   static_cast<MessageLoopResource*>(value)->DetachFromThread();
205 }
206 
207 // -----------------------------------------------------------------------------
208 
Create(PP_Instance instance)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 
GetForMainThread()218 PP_Resource GetForMainThread() {
219   ProxyAutoLock lock;
220   return PluginGlobals::Get()->loop_for_main_thread()->GetReference();
221 }
222 
GetCurrent()223 PP_Resource GetCurrent() {
224   ProxyAutoLock lock;
225   Resource* resource = MessageLoopResource::GetCurrent();
226   if (resource)
227     return resource->GetReference();
228   return 0;
229 }
230 
AttachToCurrentThread(PP_Resource message_loop)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 
Run(PP_Resource message_loop)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 
PostWork(PP_Resource message_loop,PP_CompletionCallback callback,int64_t delay_ms)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 
PostQuit(PP_Resource message_loop,PP_Bool should_destroy)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 
PPB_MessageLoop_Proxy(Dispatcher * dispatcher)271 PPB_MessageLoop_Proxy::PPB_MessageLoop_Proxy(Dispatcher* dispatcher)
272     : InterfaceProxy(dispatcher) {
273 }
274 
~PPB_MessageLoop_Proxy()275 PPB_MessageLoop_Proxy::~PPB_MessageLoop_Proxy() {
276 }
277 
278 // static
GetInterface()279 const PPB_MessageLoop_1_0* PPB_MessageLoop_Proxy::GetInterface() {
280   return &ppb_message_loop_interface;
281 }
282 
283 }  // namespace proxy
284 }  // namespace ppapi
285