| // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "ppapi/proxy/ppb_message_loop_proxy.h" | 
 |  | 
 | #include <stddef.h> | 
 |  | 
 | #include <vector> | 
 |  | 
 | #include "base/bind.h" | 
 | #include "base/bind_helpers.h" | 
 | #include "base/compiler_specific.h" | 
 | #include "base/logging.h" | 
 | #include "base/threading/thread_task_runner_handle.h" | 
 | #include "ppapi/c/pp_errors.h" | 
 | #include "ppapi/c/ppb_message_loop.h" | 
 | #include "ppapi/proxy/plugin_dispatcher.h" | 
 | #include "ppapi/proxy/plugin_globals.h" | 
 | #include "ppapi/shared_impl/proxy_lock.h" | 
 | #include "ppapi/thunk/enter.h" | 
 |  | 
 | using ppapi::thunk::PPB_MessageLoop_API; | 
 |  | 
 | namespace ppapi { | 
 | namespace proxy { | 
 |  | 
 | namespace { | 
 | typedef thunk::EnterResource<PPB_MessageLoop_API> EnterMessageLoop; | 
 | } | 
 |  | 
 | MessageLoopResource::MessageLoopResource(PP_Instance instance) | 
 |     : MessageLoopShared(instance), | 
 |       nested_invocations_(0), | 
 |       destroyed_(false), | 
 |       should_destroy_(false), | 
 |       is_main_thread_loop_(false), | 
 |       currently_handling_blocking_message_(false) { | 
 | } | 
 |  | 
 | MessageLoopResource::MessageLoopResource(ForMainThread for_main_thread) | 
 |     : MessageLoopShared(for_main_thread), | 
 |       nested_invocations_(0), | 
 |       destroyed_(false), | 
 |       should_destroy_(false), | 
 |       is_main_thread_loop_(true), | 
 |       currently_handling_blocking_message_(false) { | 
 |   // We attach the main thread immediately. We can't use AttachToCurrentThread, | 
 |   // because the MessageLoop already exists. | 
 |  | 
 |   // This must be called only once, so the slot must be empty. | 
 |   CHECK(!PluginGlobals::Get()->msg_loop_slot()); | 
 |   // We don't add a reference for TLS here, so we don't release it. Instead, | 
 |   // this loop is owned by PluginGlobals. Contrast with AttachToCurrentThread | 
 |   // where we register ReleaseMessageLoop with TLS and call AddRef. | 
 |   base::ThreadLocalStorage::Slot* slot = new base::ThreadLocalStorage::Slot(); | 
 |   PluginGlobals::Get()->set_msg_loop_slot(slot); | 
 |  | 
 |   slot->Set(this); | 
 |  | 
 |   task_runner_ = base::ThreadTaskRunnerHandle::Get(); | 
 | } | 
 |  | 
 |  | 
 | MessageLoopResource::~MessageLoopResource() { | 
 | } | 
 |  | 
 | PPB_MessageLoop_API* MessageLoopResource::AsPPB_MessageLoop_API() { | 
 |   return this; | 
 | } | 
 |  | 
 | int32_t MessageLoopResource::AttachToCurrentThread() { | 
 |   if (is_main_thread_loop_) | 
 |     return PP_ERROR_INPROGRESS; | 
 |  | 
 |   PluginGlobals* globals = PluginGlobals::Get(); | 
 |  | 
 |   base::ThreadLocalStorage::Slot* slot = globals->msg_loop_slot(); | 
 |   if (!slot) { | 
 |     slot = new base::ThreadLocalStorage::Slot(&ReleaseMessageLoop); | 
 |     globals->set_msg_loop_slot(slot); | 
 |   } else { | 
 |     if (slot->Get()) | 
 |       return PP_ERROR_INPROGRESS; | 
 |   } | 
 |   // TODO(dmichael) check that the current thread can support a message loop. | 
 |  | 
 |   // Take a ref to the MessageLoop on behalf of the TLS. Note that this is an | 
 |   // internal ref and not a plugin ref so the plugin can't accidentally | 
 |   // release it. This is released by ReleaseMessageLoop(). | 
 |   AddRef(); | 
 |   slot->Set(this); | 
 |  | 
 |   loop_.reset(new base::MessageLoop); | 
 |   task_runner_ = base::ThreadTaskRunnerHandle::Get(); | 
 |  | 
 |   // Post all pending work to the message loop. | 
 |   for (size_t i = 0; i < pending_tasks_.size(); i++) { | 
 |     const TaskInfo& info = pending_tasks_[i]; | 
 |     PostClosure(info.from_here, info.closure, info.delay_ms); | 
 |   } | 
 |   pending_tasks_.clear(); | 
 |  | 
 |   return PP_OK; | 
 | } | 
 |  | 
 | int32_t MessageLoopResource::Run() { | 
 |   if (!IsCurrent()) | 
 |     return PP_ERROR_WRONG_THREAD; | 
 |   if (is_main_thread_loop_) | 
 |     return PP_ERROR_INPROGRESS; | 
 |  | 
 |   base::RunLoop* previous_run_loop = run_loop_; | 
 |   base::RunLoop run_loop; | 
 |   run_loop_ = &run_loop; | 
 |  | 
 |   nested_invocations_++; | 
 |   CallWhileUnlocked( | 
 |       base::Bind(&base::RunLoop::Run, base::Unretained(run_loop_))); | 
 |   nested_invocations_--; | 
 |  | 
 |   run_loop_ = previous_run_loop; | 
 |  | 
 |   if (should_destroy_ && nested_invocations_ == 0) { | 
 |     task_runner_ = NULL; | 
 |     loop_.reset(); | 
 |     destroyed_ = true; | 
 |   } | 
 |   return PP_OK; | 
 | } | 
 |  | 
 | int32_t MessageLoopResource::PostWork(PP_CompletionCallback callback, | 
 |                                       int64_t delay_ms) { | 
 |   if (!callback.func) | 
 |     return PP_ERROR_BADARGUMENT; | 
 |   if (destroyed_) | 
 |     return PP_ERROR_FAILED; | 
 |   PostClosure(FROM_HERE, | 
 |               base::Bind(callback.func, callback.user_data, | 
 |                          static_cast<int32_t>(PP_OK)), | 
 |               delay_ms); | 
 |   return PP_OK; | 
 | } | 
 |  | 
 | int32_t MessageLoopResource::PostQuit(PP_Bool should_destroy) { | 
 |   if (is_main_thread_loop_) | 
 |     return PP_ERROR_WRONG_THREAD; | 
 |  | 
 |   if (PP_ToBool(should_destroy)) | 
 |     should_destroy_ = true; | 
 |  | 
 |   if (IsCurrent() && nested_invocations_ > 0) { | 
 |     run_loop_->QuitWhenIdle(); | 
 |   } else { | 
 |     PostClosure(FROM_HERE, base::Bind(&MessageLoopResource::QuitRunLoopWhenIdle, | 
 |                                       Unretained(this)), | 
 |                 0); | 
 |   } | 
 |   return PP_OK; | 
 | } | 
 |  | 
 | // static | 
 | MessageLoopResource* MessageLoopResource::GetCurrent() { | 
 |   PluginGlobals* globals = PluginGlobals::Get(); | 
 |   if (!globals->msg_loop_slot()) | 
 |     return NULL; | 
 |   return reinterpret_cast<MessageLoopResource*>( | 
 |       globals->msg_loop_slot()->Get()); | 
 | } | 
 |  | 
 | void MessageLoopResource::DetachFromThread() { | 
 |   // Note that the message loop must be destroyed on the thread it was created | 
 |   // on. | 
 |   task_runner_ = NULL; | 
 |   loop_.reset(); | 
 |  | 
 |   // Cancel out the AddRef in AttachToCurrentThread(). | 
 |   Release(); | 
 |   // DANGER: may delete this. | 
 | } | 
 |  | 
 | bool MessageLoopResource::IsCurrent() const { | 
 |   PluginGlobals* globals = PluginGlobals::Get(); | 
 |   if (!globals->msg_loop_slot()) | 
 |     return false;  // Can't be current if there's nothing in the slot. | 
 |   return static_cast<const void*>(globals->msg_loop_slot()->Get()) == | 
 |          static_cast<const void*>(this); | 
 | } | 
 |  | 
 | void MessageLoopResource::PostClosure( | 
 |     const tracked_objects::Location& from_here, | 
 |     const base::Closure& closure, | 
 |     int64_t delay_ms) { | 
 |   if (task_runner_.get()) { | 
 |     task_runner_->PostDelayedTask(from_here, closure, | 
 |                                   base::TimeDelta::FromMilliseconds(delay_ms)); | 
 |   } else { | 
 |     TaskInfo info; | 
 |     info.from_here = FROM_HERE; | 
 |     info.closure = closure; | 
 |     info.delay_ms = delay_ms; | 
 |     pending_tasks_.push_back(info); | 
 |   } | 
 | } | 
 |  | 
 | base::SingleThreadTaskRunner* MessageLoopResource::GetTaskRunner() { | 
 |   return task_runner_.get(); | 
 | } | 
 |  | 
 | bool MessageLoopResource::CurrentlyHandlingBlockingMessage() { | 
 |   return currently_handling_blocking_message_; | 
 | } | 
 |  | 
 | void MessageLoopResource::QuitRunLoopWhenIdle() { | 
 |   DCHECK(IsCurrent()); | 
 |   DCHECK(run_loop_); | 
 |   run_loop_->QuitWhenIdle(); | 
 | } | 
 |  | 
 | // static | 
 | void MessageLoopResource::ReleaseMessageLoop(void* value) { | 
 |   static_cast<MessageLoopResource*>(value)->DetachFromThread(); | 
 | } | 
 |  | 
 | // ----------------------------------------------------------------------------- | 
 |  | 
 | PP_Resource Create(PP_Instance instance) { | 
 |   ProxyAutoLock lock; | 
 |   // Validate the instance. | 
 |   PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance); | 
 |   if (!dispatcher) | 
 |     return 0; | 
 |   return (new MessageLoopResource(instance))->GetReference(); | 
 | } | 
 |  | 
 | PP_Resource GetForMainThread() { | 
 |   ProxyAutoLock lock; | 
 |   return PluginGlobals::Get()->loop_for_main_thread()->GetReference(); | 
 | } | 
 |  | 
 | PP_Resource GetCurrent() { | 
 |   ProxyAutoLock lock; | 
 |   Resource* resource = MessageLoopResource::GetCurrent(); | 
 |   if (resource) | 
 |     return resource->GetReference(); | 
 |   return 0; | 
 | } | 
 |  | 
 | int32_t AttachToCurrentThread(PP_Resource message_loop) { | 
 |   EnterMessageLoop enter(message_loop, true); | 
 |   if (enter.succeeded()) | 
 |     return enter.object()->AttachToCurrentThread(); | 
 |   return PP_ERROR_BADRESOURCE; | 
 | } | 
 |  | 
 | int32_t Run(PP_Resource message_loop) { | 
 |   EnterMessageLoop enter(message_loop, true); | 
 |   if (enter.succeeded()) | 
 |     return enter.object()->Run(); | 
 |   return PP_ERROR_BADRESOURCE; | 
 | } | 
 |  | 
 | int32_t PostWork(PP_Resource message_loop, | 
 |                  PP_CompletionCallback callback, | 
 |                  int64_t delay_ms) { | 
 |   EnterMessageLoop enter(message_loop, true); | 
 |   if (enter.succeeded()) | 
 |     return enter.object()->PostWork(callback, delay_ms); | 
 |   return PP_ERROR_BADRESOURCE; | 
 | } | 
 |  | 
 | int32_t PostQuit(PP_Resource message_loop, PP_Bool should_destroy) { | 
 |   EnterMessageLoop enter(message_loop, true); | 
 |   if (enter.succeeded()) | 
 |     return enter.object()->PostQuit(should_destroy); | 
 |   return PP_ERROR_BADRESOURCE; | 
 | } | 
 |  | 
 | const PPB_MessageLoop_1_0 ppb_message_loop_interface = { | 
 |   &Create, | 
 |   &GetForMainThread, | 
 |   &GetCurrent, | 
 |   &AttachToCurrentThread, | 
 |   &Run, | 
 |   &PostWork, | 
 |   &PostQuit | 
 | }; | 
 |  | 
 | PPB_MessageLoop_Proxy::PPB_MessageLoop_Proxy(Dispatcher* dispatcher) | 
 |     : InterfaceProxy(dispatcher) { | 
 | } | 
 |  | 
 | PPB_MessageLoop_Proxy::~PPB_MessageLoop_Proxy() { | 
 | } | 
 |  | 
 | // static | 
 | const PPB_MessageLoop_1_0* PPB_MessageLoop_Proxy::GetInterface() { | 
 |   return &ppb_message_loop_interface; | 
 | } | 
 |  | 
 | }  // namespace proxy | 
 | }  // namespace ppapi |