Elim cr-checkbox
[chromium-blink-merge.git] / ppapi / proxy / ppb_message_loop_proxy.cc
blob3d29a7b9a89201f1e700bda0d28a194d39117f24
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"
7 #include <vector>
9 #include "base/bind.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;
22 namespace ppapi {
23 namespace proxy {
25 namespace {
26 typedef thunk::EnterResource<PPB_MessageLoop_API> EnterMessageLoop;
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) {
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.
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);
56 slot->Set(this);
58 task_runner_ = base::ThreadTaskRunnerHandle::Get();
62 MessageLoopResource::~MessageLoopResource() {
65 PPB_MessageLoop_API* MessageLoopResource::AsPPB_MessageLoop_API() {
66 return this;
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();
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;
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().
88 AddRef();
89 slot->Set(this);
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();
101 return PP_OK;
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;
110 nested_invocations_++;
111 CallWhileUnlocked(
112 base::Bind(&base::MessageLoop::Run, base::Unretained(loop_.get())));
113 nested_invocations_--;
115 if (should_destroy_ && nested_invocations_ == 0) {
116 task_runner_ = NULL;
117 loop_.reset();
118 destroyed_ = true;
120 return PP_OK;
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;
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)
144 loop_->Quit();
145 else
146 PostClosure(FROM_HERE, base::MessageLoop::QuitClosure(), 0);
147 return PP_OK;
150 // static
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());
159 void MessageLoopResource::DetachFromThread() {
160 // Note that the message loop must be destroyed on the thread it was created
161 // on.
162 task_runner_ = NULL;
163 loop_.reset();
165 // Cancel out the AddRef in AttachToCurrentThread().
166 Release();
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,
181 int64 delay_ms) {
182 if (task_runner_.get()) {
183 task_runner_->PostDelayedTask(from_here, closure,
184 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);
194 base::SingleThreadTaskRunner* MessageLoopResource::GetTaskRunner() {
195 return task_runner_.get();
198 bool MessageLoopResource::CurrentlyHandlingBlockingMessage() {
199 return currently_handling_blocking_message_;
202 // static
203 void MessageLoopResource::ReleaseMessageLoop(void* value) {
204 static_cast<MessageLoopResource*>(value)->DetachFromThread();
207 // -----------------------------------------------------------------------------
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();
218 PP_Resource GetForMainThread() {
219 ProxyAutoLock lock;
220 return PluginGlobals::Get()->loop_for_main_thread()->GetReference();
223 PP_Resource GetCurrent() {
224 ProxyAutoLock lock;
225 Resource* resource = MessageLoopResource::GetCurrent();
226 if (resource)
227 return resource->GetReference();
228 return 0;
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,
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;
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 = {
262 &Create,
263 &GetForMainThread,
264 &GetCurrent,
265 &AttachToCurrentThread,
266 &Run,
267 &PostWork,
268 &PostQuit
271 PPB_MessageLoop_Proxy::PPB_MessageLoop_Proxy(Dispatcher* dispatcher)
272 : InterfaceProxy(dispatcher) {
275 PPB_MessageLoop_Proxy::~PPB_MessageLoop_Proxy() {
278 // static
279 const PPB_MessageLoop_1_0* PPB_MessageLoop_Proxy::GetInterface() {
280 return &ppb_message_loop_interface;
283 } // namespace proxy
284 } // namespace ppapi