1 // Copyright 2012 The Chromium Authors
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"
12 #include "base/bind.h"
13 #include "base/callback_helpers.h"
14 #include "base/check.h"
15 #include "base/compiler_specific.h"
16 #include "base/threading/thread_task_runner_handle.h"
17 #include "ppapi/c/pp_errors.h"
18 #include "ppapi/c/ppb_message_loop.h"
19 #include "ppapi/proxy/plugin_dispatcher.h"
20 #include "ppapi/proxy/plugin_globals.h"
21 #include "ppapi/shared_impl/proxy_lock.h"
22 #include "ppapi/thunk/enter.h"
24 using ppapi::thunk::PPB_MessageLoop_API;
30 typedef thunk::EnterResource<PPB_MessageLoop_API> EnterMessageLoop;
33 MessageLoopResource::MessageLoopResource(PP_Instance instance)
34 : MessageLoopShared(instance),
35 nested_invocations_(0),
37 should_destroy_(false),
38 is_main_thread_loop_(false),
39 currently_handling_blocking_message_(false) {
42 MessageLoopResource::MessageLoopResource(ForMainThread for_main_thread)
43 : MessageLoopShared(for_main_thread),
44 nested_invocations_(0),
46 should_destroy_(false),
47 is_main_thread_loop_(true),
48 currently_handling_blocking_message_(false) {
49 // We attach the main thread immediately. We can't use AttachToCurrentThread,
50 // because the MessageLoop already exists.
52 // This must be called only once, so the slot must be empty.
53 CHECK(!PluginGlobals::Get()->msg_loop_slot());
54 // We don't add a reference for TLS here, so we don't release it. Instead,
55 // this loop is owned by PluginGlobals. Contrast with AttachToCurrentThread
56 // where we register ReleaseMessageLoop with TLS and call AddRef.
57 base::ThreadLocalStorage::Slot* slot = new base::ThreadLocalStorage::Slot();
58 PluginGlobals::Get()->set_msg_loop_slot(slot);
62 task_runner_ = base::ThreadTaskRunnerHandle::Get();
66 MessageLoopResource::~MessageLoopResource() {
69 PPB_MessageLoop_API* MessageLoopResource::AsPPB_MessageLoop_API() {
73 int32_t MessageLoopResource::AttachToCurrentThread() {
74 if (is_main_thread_loop_) {
75 LOG(ERROR) << "Cannot attach to current thread if it is main thread loop";
76 return PP_ERROR_INPROGRESS;
79 PluginGlobals* globals = PluginGlobals::Get();
81 base::ThreadLocalStorage::Slot* slot = globals->msg_loop_slot();
83 slot = new base::ThreadLocalStorage::Slot(&ReleaseMessageLoop);
84 globals->set_msg_loop_slot(slot);
87 LOG(ERROR) << "Message loop slot already in use";
88 return PP_ERROR_INPROGRESS;
91 // TODO(dmichael) check that the current thread can support a task executor.
93 // Take a ref to the MessageLoop on behalf of the TLS. Note that this is an
94 // internal ref and not a plugin ref so the plugin can't accidentally
95 // release it. This is released by ReleaseMessageLoop().
99 single_thread_task_executor_ =
100 std::make_unique<base::SingleThreadTaskExecutor>();
101 task_runner_ = base::ThreadTaskRunnerHandle::Get();
103 // Post all pending work to the task executor.
104 for (auto& info : pending_tasks_) {
105 PostClosure(info.from_here, std::move(info.closure), info.delay_ms);
107 pending_tasks_.clear();
112 int32_t MessageLoopResource::Run() {
114 LOG(ERROR) << "Run called not on current thread";
115 return PP_ERROR_WRONG_THREAD;
117 if (is_main_thread_loop_) {
118 LOG(ERROR) << "Run called on the main thread";
119 return PP_ERROR_INPROGRESS;
122 base::RunLoop* previous_run_loop = run_loop_;
123 base::RunLoop run_loop;
124 run_loop_ = &run_loop;
126 nested_invocations_++;
127 CallWhileUnlocked(base::BindOnce(&base::RunLoop::Run,
128 base::Unretained(run_loop_), FROM_HERE));
129 nested_invocations_--;
131 run_loop_ = previous_run_loop;
133 if (should_destroy_ && nested_invocations_ == 0) {
134 task_runner_.reset();
136 // Message Loop dtor may try to aquire lock.
137 ProxyAutoUnlock lock;
138 single_thread_task_executor_.reset();
145 int32_t MessageLoopResource::PostWork(PP_CompletionCallback callback,
147 if (!callback.func) {
149 return PP_ERROR_BADARGUMENT;
152 LOG(ERROR) << "Message loop already destroyed";
153 return PP_ERROR_FAILED;
155 PostClosure(FROM_HERE,
156 base::BindOnce(callback.func, callback.user_data,
157 static_cast<int32_t>(PP_OK)),
162 int32_t MessageLoopResource::PostQuit(PP_Bool should_destroy) {
163 if (is_main_thread_loop_) {
164 LOG(ERROR) << "Post Quit in main thread loop";
165 return PP_ERROR_WRONG_THREAD;
168 if (PP_ToBool(should_destroy))
169 should_destroy_ = true;
171 if (IsCurrent() && nested_invocations_ > 0) {
172 run_loop_->QuitWhenIdle();
174 PostClosure(FROM_HERE,
175 base::BindOnce(&MessageLoopResource::QuitRunLoopWhenIdle,
183 MessageLoopResource* MessageLoopResource::GetCurrent() {
184 PluginGlobals* globals = PluginGlobals::Get();
185 if (!globals->msg_loop_slot())
187 return reinterpret_cast<MessageLoopResource*>(
188 globals->msg_loop_slot()->Get());
191 void MessageLoopResource::DetachFromThread() {
192 // Note that the task executor must be destroyed on the thread it was created
194 task_runner_.reset();
195 single_thread_task_executor_.reset();
197 // Cancel out the AddRef in AttachToCurrentThread().
199 // DANGER: may delete this.
202 bool MessageLoopResource::IsCurrent() const {
203 PluginGlobals* globals = PluginGlobals::Get();
204 if (!globals->msg_loop_slot())
205 return false; // Can't be current if there's nothing in the slot.
206 return static_cast<const void*>(globals->msg_loop_slot()->Get()) ==
207 static_cast<const void*>(this);
210 void MessageLoopResource::PostClosure(const base::Location& from_here,
211 base::OnceClosure closure,
213 if (task_runner_.get()) {
214 task_runner_->PostDelayedTask(from_here, std::move(closure),
215 base::Milliseconds(delay_ms));
218 info.from_here = FROM_HERE;
219 info.closure = std::move(closure);
220 info.delay_ms = delay_ms;
221 pending_tasks_.push_back(std::move(info));
225 base::SingleThreadTaskRunner* MessageLoopResource::GetTaskRunner() {
226 return task_runner_.get();
229 bool MessageLoopResource::CurrentlyHandlingBlockingMessage() {
230 return currently_handling_blocking_message_;
233 void MessageLoopResource::QuitRunLoopWhenIdle() {
236 run_loop_->QuitWhenIdle();
240 void MessageLoopResource::ReleaseMessageLoop(void* value) {
241 static_cast<MessageLoopResource*>(value)->DetachFromThread();
244 // -----------------------------------------------------------------------------
246 PP_Resource Create(PP_Instance instance) {
248 // Validate the instance.
249 PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance);
252 return (new MessageLoopResource(instance))->GetReference();
255 PP_Resource GetForMainThread() {
257 return PluginGlobals::Get()->loop_for_main_thread()->GetReference();
260 PP_Resource GetCurrent() {
262 Resource* resource = MessageLoopResource::GetCurrent();
264 return resource->GetReference();
268 int32_t AttachToCurrentThread(PP_Resource message_loop) {
269 EnterMessageLoop enter(message_loop, true);
270 if (enter.succeeded())
271 return enter.object()->AttachToCurrentThread();
273 return PP_ERROR_BADRESOURCE;
276 int32_t Run(PP_Resource message_loop) {
277 EnterMessageLoop enter(message_loop, true);
278 if (enter.succeeded())
279 return enter.object()->Run();
280 LOG(ERROR) << "Cannot enter message loop";
281 return PP_ERROR_BADRESOURCE;
284 int32_t PostWork(PP_Resource message_loop,
285 PP_CompletionCallback callback,
287 EnterMessageLoop enter(message_loop, true);
288 if (enter.succeeded())
289 return enter.object()->PostWork(callback, delay_ms);
290 LOG(ERROR) << "Cannot enter message loop";
291 return PP_ERROR_BADRESOURCE;
294 int32_t PostQuit(PP_Resource message_loop, PP_Bool should_destroy) {
295 EnterMessageLoop enter(message_loop, true);
296 if (enter.succeeded())
297 return enter.object()->PostQuit(should_destroy);
298 LOG(ERROR) << "Cannot enter message loop";
299 return PP_ERROR_BADRESOURCE;
302 const PPB_MessageLoop_1_0 ppb_message_loop_interface = {
306 &AttachToCurrentThread,
312 PPB_MessageLoop_Proxy::PPB_MessageLoop_Proxy(Dispatcher* dispatcher)
313 : InterfaceProxy(dispatcher) {
316 PPB_MessageLoop_Proxy::~PPB_MessageLoop_Proxy() {
320 const PPB_MessageLoop_1_0* PPB_MessageLoop_Proxy::GetInterface() {
321 return &ppb_message_loop_interface;