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