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/location.h"
12 #include "base/thread_task_runner_handle.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),
35 currently_handling_blocking_message_(false) {
38 MessageLoopResource::MessageLoopResource(ForMainThread for_main_thread
)
39 : MessageLoopShared(for_main_thread
),
40 nested_invocations_(0),
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.
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
);
58 task_runner_
= base::ThreadTaskRunnerHandle::Get();
62 MessageLoopResource::~MessageLoopResource() {
65 PPB_MessageLoop_API
* MessageLoopResource::AsPPB_MessageLoop_API() {
69 int32_t MessageLoopResource::AttachToCurrentThread() {
70 if (is_main_thread_loop_
)
71 return PP_ERROR_INPROGRESS
;
73 PluginGlobals
* globals
= PluginGlobals::Get();
75 base::ThreadLocalStorage::Slot
* slot
= globals
->msg_loop_slot();
77 slot
= new base::ThreadLocalStorage::Slot(&ReleaseMessageLoop
);
78 globals
->set_msg_loop_slot(slot
);
81 return PP_ERROR_INPROGRESS
;
83 // TODO(dmichael) check that the current thread can support a message loop.
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().
91 loop_
.reset(new base::MessageLoop
);
92 task_runner_
= base::ThreadTaskRunnerHandle::Get();
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
);
99 pending_tasks_
.clear();
104 int32_t MessageLoopResource::Run() {
106 return PP_ERROR_WRONG_THREAD
;
107 if (is_main_thread_loop_
)
108 return PP_ERROR_INPROGRESS
;
110 nested_invocations_
++;
112 base::Bind(&base::MessageLoop::Run
, base::Unretained(loop_
.get())));
113 nested_invocations_
--;
115 if (should_destroy_
&& nested_invocations_
== 0) {
123 int32_t MessageLoopResource::PostWork(PP_CompletionCallback callback
,
126 return PP_ERROR_BADARGUMENT
;
128 return PP_ERROR_FAILED
;
129 PostClosure(FROM_HERE
,
130 base::Bind(callback
.func
, callback
.user_data
,
131 static_cast<int32_t>(PP_OK
)),
136 int32_t MessageLoopResource::PostQuit(PP_Bool should_destroy
) {
137 if (is_main_thread_loop_
)
138 return PP_ERROR_WRONG_THREAD
;
140 if (PP_ToBool(should_destroy
))
141 should_destroy_
= true;
143 if (IsCurrent() && nested_invocations_
> 0)
146 PostClosure(FROM_HERE
, base::MessageLoop::QuitClosure(), 0);
151 MessageLoopResource
* MessageLoopResource::GetCurrent() {
152 PluginGlobals
* globals
= PluginGlobals::Get();
153 if (!globals
->msg_loop_slot())
155 return reinterpret_cast<MessageLoopResource
*>(
156 globals
->msg_loop_slot()->Get());
159 void MessageLoopResource::DetachFromThread() {
160 // Note that the message loop must be destroyed on the thread it was created
165 // Cancel out the AddRef in AttachToCurrentThread().
167 // DANGER: may delete this.
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);
178 void MessageLoopResource::PostClosure(
179 const tracked_objects::Location
& from_here
,
180 const base::Closure
& closure
,
182 if (task_runner_
.get()) {
183 task_runner_
->PostDelayedTask(from_here
, closure
,
184 base::TimeDelta::FromMilliseconds(delay_ms
));
187 info
.from_here
= FROM_HERE
;
188 info
.closure
= closure
;
189 info
.delay_ms
= delay_ms
;
190 pending_tasks_
.push_back(info
);
194 base::SingleThreadTaskRunner
* MessageLoopResource::GetTaskRunner() {
195 return task_runner_
.get();
198 bool MessageLoopResource::CurrentlyHandlingBlockingMessage() {
199 return currently_handling_blocking_message_
;
203 void MessageLoopResource::ReleaseMessageLoop(void* value
) {
204 static_cast<MessageLoopResource
*>(value
)->DetachFromThread();
207 // -----------------------------------------------------------------------------
209 PP_Resource
Create(PP_Instance instance
) {
211 // Validate the instance.
212 PluginDispatcher
* dispatcher
= PluginDispatcher::GetForInstance(instance
);
215 return (new MessageLoopResource(instance
))->GetReference();
218 PP_Resource
GetForMainThread() {
220 return PluginGlobals::Get()->loop_for_main_thread()->GetReference();
223 PP_Resource
GetCurrent() {
225 Resource
* resource
= MessageLoopResource::GetCurrent();
227 return resource
->GetReference();
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
;
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
;
245 int32_t PostWork(PP_Resource message_loop
,
246 PP_CompletionCallback callback
,
248 EnterMessageLoop
enter(message_loop
, true);
249 if (enter
.succeeded())
250 return enter
.object()->PostWork(callback
, delay_ms
);
251 return PP_ERROR_BADRESOURCE
;
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
;
261 const PPB_MessageLoop_1_0 ppb_message_loop_interface
= {
265 &AttachToCurrentThread
,
271 PPB_MessageLoop_Proxy::PPB_MessageLoop_Proxy(Dispatcher
* dispatcher
)
272 : InterfaceProxy(dispatcher
) {
275 PPB_MessageLoop_Proxy::~PPB_MessageLoop_Proxy() {
279 const PPB_MessageLoop_1_0
* PPB_MessageLoop_Proxy::GetInterface() {
280 return &ppb_message_loop_interface
;