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