Home | History | Annotate | Download | only in proxy
      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 
     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 
     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 
     62 MessageLoopResource::~MessageLoopResource() {
     63 }
     64 
     65 PPB_MessageLoop_API* MessageLoopResource::AsPPB_MessageLoop_API() {
     66   return this;
     67 }
     68 
     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 
    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 
    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 
    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
    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 
    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 
    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);
    176 }
    177 
    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 
    194 base::MessageLoopProxy* MessageLoopResource::GetMessageLoopProxy() {
    195   return loop_proxy_.get();
    196 }
    197 
    198 bool MessageLoopResource::CurrentlyHandlingBlockingMessage() {
    199   return currently_handling_blocking_message_;
    200 }
    201 
    202 // static
    203 void MessageLoopResource::ReleaseMessageLoop(void* value) {
    204   static_cast<MessageLoopResource*>(value)->DetachFromThread();
    205 }
    206 
    207 // -----------------------------------------------------------------------------
    208 
    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 
    218 PP_Resource GetForMainThread() {
    219   ProxyAutoLock lock;
    220   return PluginGlobals::Get()->loop_for_main_thread()->GetReference();
    221 }
    222 
    223 PP_Resource GetCurrent() {
    224   ProxyAutoLock lock;
    225   Resource* resource = MessageLoopResource::GetCurrent();
    226   if (resource)
    227     return resource->GetReference();
    228   return 0;
    229 }
    230 
    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 
    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 
    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 
    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 
    271 PPB_MessageLoop_Proxy::PPB_MessageLoop_Proxy(Dispatcher* dispatcher)
    272     : InterfaceProxy(dispatcher) {
    273 }
    274 
    275 PPB_MessageLoop_Proxy::~PPB_MessageLoop_Proxy() {
    276 }
    277 
    278 // static
    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