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
20using ppapi::thunk::PPB_MessageLoop_API;
21
22namespace ppapi {
23namespace proxy {
24
25namespace {
26typedef thunk::EnterResource<PPB_MessageLoop_API> EnterMessageLoop;
27}
28
29MessageLoopResource::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}
36
37MessageLoopResource::MessageLoopResource(ForMainThread for_main_thread)
38    : MessageLoopShared(for_main_thread),
39      nested_invocations_(0),
40      destroyed_(false),
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.
45
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);
53
54  slot->Set(this);
55
56  loop_proxy_ = base::MessageLoopProxy::current();
57}
58
59
60MessageLoopResource::~MessageLoopResource() {
61}
62
63PPB_MessageLoop_API* MessageLoopResource::AsPPB_MessageLoop_API() {
64  return this;
65}
66
67int32_t MessageLoopResource::AttachToCurrentThread() {
68  if (is_main_thread_loop_)
69    return PP_ERROR_INPROGRESS;
70
71  PluginGlobals* globals = PluginGlobals::Get();
72
73  base::ThreadLocalStorage::Slot* slot = globals->msg_loop_slot();
74  if (!slot) {
75    slot = new base::ThreadLocalStorage::Slot(&ReleaseMessageLoop);
76    globals->set_msg_loop_slot(slot);
77  } else {
78    if (slot->Get())
79      return PP_ERROR_INPROGRESS;
80  }
81  // TODO(dmichael) check that the current thread can support a message loop.
82
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().
86  AddRef();
87  slot->Set(this);
88
89  loop_.reset(new base::MessageLoop(base::MessageLoop::TYPE_DEFAULT));
90  loop_proxy_ = base::MessageLoopProxy::current();
91
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);
96  }
97  pending_tasks_.clear();
98
99  return PP_OK;
100}
101
102int32_t MessageLoopResource::Run() {
103  if (!IsCurrent())
104    return PP_ERROR_WRONG_THREAD;
105  if (is_main_thread_loop_)
106    return PP_ERROR_INPROGRESS;
107
108  nested_invocations_++;
109  CallWhileUnlocked(
110      base::Bind(&base::MessageLoop::Run, base::Unretained(loop_.get())));
111  nested_invocations_--;
112
113  if (should_destroy_ && nested_invocations_ == 0) {
114    loop_proxy_ = NULL;
115    loop_.reset();
116    destroyed_ = true;
117  }
118  return PP_OK;
119}
120
121int32_t MessageLoopResource::PostWork(PP_CompletionCallback callback,
122                                      int64_t delay_ms) {
123  if (!callback.func)
124    return PP_ERROR_BADARGUMENT;
125  if (destroyed_)
126    return PP_ERROR_FAILED;
127  PostClosure(FROM_HERE,
128              base::Bind(callback.func, callback.user_data,
129                         static_cast<int32_t>(PP_OK)),
130              delay_ms);
131  return PP_OK;
132}
133
134int32_t MessageLoopResource::PostQuit(PP_Bool should_destroy) {
135  if (is_main_thread_loop_)
136    return PP_ERROR_WRONG_THREAD;
137
138  if (PP_ToBool(should_destroy))
139    should_destroy_ = true;
140
141  if (IsCurrent() && nested_invocations_ > 0)
142    loop_->Quit();
143  else
144    PostClosure(FROM_HERE, base::MessageLoop::QuitClosure(), 0);
145  return PP_OK;
146}
147
148// static
149MessageLoopResource* MessageLoopResource::GetCurrent() {
150  PluginGlobals* globals = PluginGlobals::Get();
151  if (!globals->msg_loop_slot())
152    return NULL;
153  return reinterpret_cast<MessageLoopResource*>(
154      globals->msg_loop_slot()->Get());
155}
156
157void MessageLoopResource::DetachFromThread() {
158  // Note that the message loop must be destroyed on the thread it was created
159  // on.
160  loop_proxy_ = NULL;
161  loop_.reset();
162
163  // Cancel out the AddRef in AttachToCurrentThread().
164  Release();
165  // DANGER: may delete this.
166}
167
168bool 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);
174}
175
176void MessageLoopResource::PostClosure(
177    const tracked_objects::Location& from_here,
178    const base::Closure& closure,
179    int64 delay_ms) {
180  if (loop_proxy_.get()) {
181    loop_proxy_->PostDelayedTask(
182        from_here, closure, base::TimeDelta::FromMilliseconds(delay_ms));
183  } else {
184    TaskInfo info;
185    info.from_here = FROM_HERE;
186    info.closure = closure;
187    info.delay_ms = delay_ms;
188    pending_tasks_.push_back(info);
189  }
190}
191
192base::MessageLoopProxy* MessageLoopResource::GetMessageLoopProxy() {
193  return loop_proxy_.get();
194}
195
196// static
197void MessageLoopResource::ReleaseMessageLoop(void* value) {
198  static_cast<MessageLoopResource*>(value)->DetachFromThread();
199}
200
201// -----------------------------------------------------------------------------
202
203PP_Resource Create(PP_Instance instance) {
204  ProxyAutoLock lock;
205  // Validate the instance.
206  PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance);
207  if (!dispatcher)
208    return 0;
209  return (new MessageLoopResource(instance))->GetReference();
210}
211
212PP_Resource GetForMainThread() {
213  ProxyAutoLock lock;
214  return PluginGlobals::Get()->loop_for_main_thread()->GetReference();
215}
216
217PP_Resource GetCurrent() {
218  ProxyAutoLock lock;
219  Resource* resource = MessageLoopResource::GetCurrent();
220  if (resource)
221    return resource->GetReference();
222  return 0;
223}
224
225int32_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;
230}
231
232int32_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;
237}
238
239int32_t PostWork(PP_Resource message_loop,
240                 PP_CompletionCallback callback,
241                 int64_t delay_ms) {
242  EnterMessageLoop enter(message_loop, true);
243  if (enter.succeeded())
244    return enter.object()->PostWork(callback, delay_ms);
245  return PP_ERROR_BADRESOURCE;
246}
247
248int32_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;
253}
254
255const PPB_MessageLoop_1_0 ppb_message_loop_interface = {
256  &Create,
257  &GetForMainThread,
258  &GetCurrent,
259  &AttachToCurrentThread,
260  &Run,
261  &PostWork,
262  &PostQuit
263};
264
265PPB_MessageLoop_Proxy::PPB_MessageLoop_Proxy(Dispatcher* dispatcher)
266    : InterfaceProxy(dispatcher) {
267}
268
269PPB_MessageLoop_Proxy::~PPB_MessageLoop_Proxy() {
270}
271
272// static
273const PPB_MessageLoop_1_0* PPB_MessageLoop_Proxy::GetInterface() {
274  return &ppb_message_loop_interface;
275}
276
277}  // namespace proxy
278}  // namespace ppapi
279