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.
5 #include "ppapi/proxy/ppb_message_loop_proxy.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"
20 using ppapi::thunk::PPB_MessageLoop_API
;
26 typedef thunk::EnterResource
<PPB_MessageLoop_API
> EnterMessageLoop
;
29 MessageLoopResource::MessageLoopResource(PP_Instance instance
)
30 : MessageLoopShared(instance
),
31 nested_invocations_(0),
33 should_destroy_(false),
34 is_main_thread_loop_(false) {
37 MessageLoopResource::MessageLoopResource(ForMainThread for_main_thread
)
38 : MessageLoopShared(for_main_thread
),
39 nested_invocations_(0),
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.
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
);
56 loop_proxy_
= base::MessageLoopProxy::current();
60 MessageLoopResource::~MessageLoopResource() {
63 PPB_MessageLoop_API
* MessageLoopResource::AsPPB_MessageLoop_API() {
67 int32_t MessageLoopResource::AttachToCurrentThread() {
68 if (is_main_thread_loop_
)
69 return PP_ERROR_INPROGRESS
;
71 PluginGlobals
* globals
= PluginGlobals::Get();
73 base::ThreadLocalStorage::Slot
* slot
= globals
->msg_loop_slot();
75 slot
= new base::ThreadLocalStorage::Slot(&ReleaseMessageLoop
);
76 globals
->set_msg_loop_slot(slot
);
79 return PP_ERROR_INPROGRESS
;
81 // TODO(dmichael) check that the current thread can support a message loop.
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().
89 loop_
.reset(new base::MessageLoop
);
90 loop_proxy_
= base::MessageLoopProxy::current();
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
);
97 pending_tasks_
.clear();
102 int32_t MessageLoopResource::Run() {
104 return PP_ERROR_WRONG_THREAD
;
105 if (is_main_thread_loop_
)
106 return PP_ERROR_INPROGRESS
;
108 nested_invocations_
++;
110 base::Bind(&base::MessageLoop::Run
, base::Unretained(loop_
.get())));
111 nested_invocations_
--;
113 if (should_destroy_
&& nested_invocations_
== 0) {
121 int32_t MessageLoopResource::PostWork(PP_CompletionCallback callback
,
124 return PP_ERROR_BADARGUMENT
;
126 return PP_ERROR_FAILED
;
127 PostClosure(FROM_HERE
,
128 base::Bind(callback
.func
, callback
.user_data
,
129 static_cast<int32_t>(PP_OK
)),
134 int32_t MessageLoopResource::PostQuit(PP_Bool should_destroy
) {
135 if (is_main_thread_loop_
)
136 return PP_ERROR_WRONG_THREAD
;
138 if (PP_ToBool(should_destroy
))
139 should_destroy_
= true;
141 if (IsCurrent() && nested_invocations_
> 0)
144 PostClosure(FROM_HERE
, base::MessageLoop::QuitClosure(), 0);
149 MessageLoopResource
* MessageLoopResource::GetCurrent() {
150 PluginGlobals
* globals
= PluginGlobals::Get();
151 if (!globals
->msg_loop_slot())
153 return reinterpret_cast<MessageLoopResource
*>(
154 globals
->msg_loop_slot()->Get());
157 void MessageLoopResource::DetachFromThread() {
158 // Note that the message loop must be destroyed on the thread it was created
163 // Cancel out the AddRef in AttachToCurrentThread().
165 // DANGER: may delete this.
168 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);
176 void MessageLoopResource::PostClosure(
177 const tracked_objects::Location
& from_here
,
178 const base::Closure
& closure
,
180 if (loop_proxy_
.get()) {
181 loop_proxy_
->PostDelayedTask(
182 from_here
, closure
, base::TimeDelta::FromMilliseconds(delay_ms
));
185 info
.from_here
= FROM_HERE
;
186 info
.closure
= closure
;
187 info
.delay_ms
= delay_ms
;
188 pending_tasks_
.push_back(info
);
192 base::MessageLoopProxy
* MessageLoopResource::GetMessageLoopProxy() {
193 return loop_proxy_
.get();
197 void MessageLoopResource::ReleaseMessageLoop(void* value
) {
198 static_cast<MessageLoopResource
*>(value
)->DetachFromThread();
201 // -----------------------------------------------------------------------------
203 PP_Resource
Create(PP_Instance instance
) {
205 // Validate the instance.
206 PluginDispatcher
* dispatcher
= PluginDispatcher::GetForInstance(instance
);
209 return (new MessageLoopResource(instance
))->GetReference();
212 PP_Resource
GetForMainThread() {
214 return PluginGlobals::Get()->loop_for_main_thread()->GetReference();
217 PP_Resource
GetCurrent() {
219 Resource
* resource
= MessageLoopResource::GetCurrent();
221 return resource
->GetReference();
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
;
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
;
239 int32_t PostWork(PP_Resource message_loop
,
240 PP_CompletionCallback callback
,
242 EnterMessageLoop
enter(message_loop
, true);
243 if (enter
.succeeded())
244 return enter
.object()->PostWork(callback
, delay_ms
);
245 return PP_ERROR_BADRESOURCE
;
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
;
255 const PPB_MessageLoop_1_0 ppb_message_loop_interface
= {
259 &AttachToCurrentThread
,
265 PPB_MessageLoop_Proxy::PPB_MessageLoop_Proxy(Dispatcher
* dispatcher
)
266 : InterfaceProxy(dispatcher
) {
269 PPB_MessageLoop_Proxy::~PPB_MessageLoop_Proxy() {
273 const PPB_MessageLoop_1_0
* PPB_MessageLoop_Proxy::GetInterface() {
274 return &ppb_message_loop_interface
;