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 }
     36 
     37 MessageLoopResource::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 
     60 MessageLoopResource::~MessageLoopResource() {
     61 }
     62 
     63 PPB_MessageLoop_API* MessageLoopResource::AsPPB_MessageLoop_API() {
     64   return this;
     65 }
     66 
     67 int32_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);
     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 
    102 int32_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 
    121 int32_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 
    134 int32_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
    149 MessageLoopResource* 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 
    157 void 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 
    168 bool 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 
    176 void 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 
    192 base::MessageLoopProxy* MessageLoopResource::GetMessageLoopProxy() {
    193   return loop_proxy_.get();
    194 }
    195 
    196 // static
    197 void MessageLoopResource::ReleaseMessageLoop(void* value) {
    198   static_cast<MessageLoopResource*>(value)->DetachFromThread();
    199 }
    200 
    201 // -----------------------------------------------------------------------------
    202 
    203 PP_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 
    212 PP_Resource GetForMainThread() {
    213   ProxyAutoLock lock;
    214   return PluginGlobals::Get()->loop_for_main_thread()->GetReference();
    215 }
    216 
    217 PP_Resource GetCurrent() {
    218   ProxyAutoLock lock;
    219   Resource* resource = MessageLoopResource::GetCurrent();
    220   if (resource)
    221     return resource->GetReference();
    222   return 0;
    223 }
    224 
    225 int32_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 
    232 int32_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 
    239 int32_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 
    248 int32_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 
    255 const PPB_MessageLoop_1_0 ppb_message_loop_interface = {
    256   &Create,
    257   &GetForMainThread,
    258   &GetCurrent,
    259   &AttachToCurrentThread,
    260   &Run,
    261   &PostWork,
    262   &PostQuit
    263 };
    264 
    265 PPB_MessageLoop_Proxy::PPB_MessageLoop_Proxy(Dispatcher* dispatcher)
    266     : InterfaceProxy(dispatcher) {
    267 }
    268 
    269 PPB_MessageLoop_Proxy::~PPB_MessageLoop_Proxy() {
    270 }
    271 
    272 // static
    273 const PPB_MessageLoop_1_0* PPB_MessageLoop_Proxy::GetInterface() {
    274   return &ppb_message_loop_interface;
    275 }
    276 
    277 }  // namespace proxy
    278 }  // namespace ppapi
    279