diff options
author | brettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-01-26 05:15:48 +0000 |
---|---|---|
committer | brettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-01-26 05:15:48 +0000 |
commit | 574c50aaf667d40981d81a046307f3057ab2eaee (patch) | |
tree | 4aa89f0e2558416ae9642c1126add05c7e6f3bc0 | |
parent | fd8f0fad7c0d359f2eb9a1ffd24e7861c45d0850 (diff) | |
download | chromium_src-574c50aaf667d40981d81a046307f3057ab2eaee.zip chromium_src-574c50aaf667d40981d81a046307f3057ab2eaee.tar.gz chromium_src-574c50aaf667d40981d81a046307f3057ab2eaee.tar.bz2 |
First pass at implementing the MessageLoop interface. This includes a simple
example and a helper class. The current example just asserts due to thread
checks we have in there now, but this should provide a good starting point.
BUG=
TEST=
Review URL: https://chromiumcodereview.appspot.com/9097006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@119198 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | ppapi/examples/threading/threading.cc | 60 | ||||
-rw-r--r-- | ppapi/ppapi_proxy.gypi | 2 | ||||
-rw-r--r-- | ppapi/ppapi_shared.gypi | 1 | ||||
-rw-r--r-- | ppapi/ppapi_sources.gypi | 2 | ||||
-rw-r--r-- | ppapi/proxy/interface_list.cc | 4 | ||||
-rw-r--r-- | ppapi/proxy/ppb_message_loop_proxy.cc | 287 | ||||
-rw-r--r-- | ppapi/proxy/ppb_message_loop_proxy.h | 30 | ||||
-rw-r--r-- | ppapi/shared_impl/resource.h | 1 | ||||
-rw-r--r-- | ppapi/thunk/ppb_message_loop_api.h | 25 | ||||
-rw-r--r-- | ppapi/utility/threading/simple_thread.cc | 96 | ||||
-rw-r--r-- | ppapi/utility/threading/simple_thread.h | 65 |
11 files changed, 573 insertions, 0 deletions
diff --git a/ppapi/examples/threading/threading.cc b/ppapi/examples/threading/threading.cc new file mode 100644 index 0000000..bfd21a9 --- /dev/null +++ b/ppapi/examples/threading/threading.cc @@ -0,0 +1,60 @@ +// 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/c/pp_errors.h" +#include "ppapi/cpp/input_event.h" +#include "ppapi/cpp/instance.h" +#include "ppapi/cpp/module.h" +#include "ppapi/utility/completion_callback_factory.h" +#include "ppapi/utility/threading/simple_thread.h" + +class MyInstance : public pp::Instance { + public: + MyInstance(PP_Instance instance) : pp::Instance(instance) { + thread_ = new pp::SimpleThread(this); + factory_.Initialize(this); + } + + virtual ~MyInstance() { + delete thread_; + } + + virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]) { + thread_->Start(); + thread_->message_loop().PostWork( + factory_.NewCallback(&MyInstance::CallOnBackground)); + return true; + } + + virtual void DidChangeView(const pp::View& view) { + } + + private: + void CallOnBackground(int32_t result) { + } + + pp::CompletionCallbackFactory<MyInstance> factory_; + + pp::SimpleThread* thread_; +}; + + +class MyModule : public pp::Module { + public: + MyModule() : pp::Module() {} + virtual ~MyModule() {} + + virtual pp::Instance* CreateInstance(PP_Instance instance) { + return new MyInstance(instance); + } +}; + +namespace pp { + +// Factory function for your specialization of the Module object. +Module* CreateModule() { + return new MyModule(); +} + +} // namespace pp diff --git a/ppapi/ppapi_proxy.gypi b/ppapi/ppapi_proxy.gypi index bd50b3b..16081a3 100644 --- a/ppapi/ppapi_proxy.gypi +++ b/ppapi/ppapi_proxy.gypi @@ -99,6 +99,8 @@ 'proxy/ppb_image_data_proxy.h', 'proxy/ppb_instance_proxy.cc', 'proxy/ppb_instance_proxy.h', + 'proxy/ppb_message_loop_proxy.cc', + 'proxy/ppb_message_loop_proxy.h', 'proxy/ppb_pdf_proxy.cc', 'proxy/ppb_pdf_proxy.h', 'proxy/ppb_tcp_socket_private_proxy.cc', diff --git a/ppapi/ppapi_shared.gypi b/ppapi/ppapi_shared.gypi index 5aff613..4dc7a0f 100644 --- a/ppapi/ppapi_shared.gypi +++ b/ppapi/ppapi_shared.gypi @@ -198,6 +198,7 @@ 'thunk/ppb_instance_thunk.cc', 'thunk/ppb_layer_compositor_api.h', 'thunk/ppb_layer_compositor_thunk.cc', + 'thunk/ppb_message_loop_api.h', 'thunk/ppb_messaging_thunk.cc', 'thunk/ppb_mouse_lock_thunk.cc', 'thunk/ppb_pdf_api.h', diff --git a/ppapi/ppapi_sources.gypi b/ppapi/ppapi_sources.gypi index 4336ff6..e7b869c 100644 --- a/ppapi/ppapi_sources.gypi +++ b/ppapi/ppapi_sources.gypi @@ -267,6 +267,8 @@ 'utility/graphics/paint_aggregator.h', 'utility/graphics/paint_manager.cc', 'utility/graphics/paint_manager.h', + 'utility/threading/simple_thread.cc', + 'utility/threading/simple_thread.h', ], # # Common Testing source for trusted and untrusted (NaCl) pugins. diff --git a/ppapi/proxy/interface_list.cc b/ppapi/proxy/interface_list.cc index f5c4d31..5833a8d 100644 --- a/ppapi/proxy/interface_list.cc +++ b/ppapi/proxy/interface_list.cc @@ -17,6 +17,7 @@ #include "ppapi/c/dev/ppb_gles_chromium_texture_mapping_dev.h" #include "ppapi/c/dev/ppb_ime_input_event_dev.h" #include "ppapi/c/dev/ppb_memory_dev.h" +#include "ppapi/c/dev/ppb_message_loop_dev.h" #include "ppapi/c/dev/ppb_resource_array_dev.h" #include "ppapi/c/dev/ppb_testing_dev.h" #include "ppapi/c/dev/ppb_text_input_dev.h" @@ -80,6 +81,7 @@ #include "ppapi/proxy/ppb_graphics_3d_proxy.h" #include "ppapi/proxy/ppb_image_data_proxy.h" #include "ppapi/proxy/ppb_instance_proxy.h" +#include "ppapi/proxy/ppb_message_loop_proxy.h" #include "ppapi/proxy/ppb_pdf_proxy.h" #include "ppapi/proxy/ppb_tcp_socket_private_proxy.h" #include "ppapi/proxy/ppb_testing_proxy.h" @@ -169,6 +171,8 @@ InterfaceList::InterfaceList() { AddProxy(API_ID_PPP_CLASS, &PPP_Class_Proxy::Create); AddPPB(PPB_CORE_INTERFACE_1_0, API_ID_PPB_CORE, PPB_Core_Proxy::GetPPB_Core_Interface()); + AddPPB(PPB_MESSAGELOOP_DEV_INTERFACE_0_1, API_ID_NONE, + PPB_MessageLoop_Proxy::GetInterface()); AddPPB(PPB_OPENGLES2_INTERFACE_1_0, API_ID_NONE, PPB_OpenGLES2_Shared::GetInterface()); AddPPB(PPB_VAR_INTERFACE_1_1, API_ID_NONE, diff --git a/ppapi/proxy/ppb_message_loop_proxy.cc b/ppapi/proxy/ppb_message_loop_proxy.cc new file mode 100644 index 0000000..5a0cf23 --- /dev/null +++ b/ppapi/proxy/ppb_message_loop_proxy.cc @@ -0,0 +1,287 @@ +// 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 <vector> + +#include "base/bind.h" +#include "base/compiler_specific.h" +#include "base/message_loop.h" +#include "base/threading/thread_local_storage.h" +#include "ppapi/c/dev/ppb_message_loop_dev.h" +#include "ppapi/c/pp_errors.h" +#include "ppapi/proxy/plugin_dispatcher.h" +#include "ppapi/shared_impl/resource.h" +#include "ppapi/thunk/enter.h" +#include "ppapi/thunk/ppb_message_loop_api.h" + +using ppapi::thunk::PPB_MessageLoop_API; + +namespace ppapi { +namespace proxy { + +namespace { + +typedef thunk::EnterResource<PPB_MessageLoop_API> EnterMessageLoop; + +static base::ThreadLocalStorage::Slot tls_slot(base::LINKER_INITIALIZED); + +class MessageLoopResource : public Resource, public PPB_MessageLoop_API { + public: + MessageLoopResource(PP_Instance instance); + virtual ~MessageLoopResource(); + + // Resource overrides. + virtual PPB_MessageLoop_API* AsPPB_MessageLoop_API() OVERRIDE; + + // PPB_MessageLoop_API implementation. + virtual int32_t AttachToCurrentThread() OVERRIDE; + virtual int32_t Run() OVERRIDE; + virtual int32_t PostWork(PP_CompletionCallback callback, + int64_t delay_ms) OVERRIDE; + virtual int32_t PostQuit(PP_Bool should_destroy) OVERRIDE; + + void DetachFromThread(); + + private: + struct TaskInfo { + tracked_objects::Location from_here; + base::Closure closure; + int64 delay_ms; + }; + + // Returns true if the object is associated with the current thread. + bool IsCurrent() const; + + // Handles posting to the message loop if there is one, or the pending queue + // if there isn't. + void PostClosure(const tracked_objects::Location& from_here, + const base::Closure& closure, + int64 delay_ms); + + // TLS destructor function. + static void ReleaseMessageLoop(void* value); + + // Created when we attach to the current thread, since MessageLoop assumes + // that it's created on the thread it will run on. + scoped_ptr<MessageLoop> loop_; + + // Number of invocations of Run currently on the stack. + int nested_invocations_; + + // Set to true when the message loop is destroyed to prevent forther + // posting of work. + bool destroyed_; + + // Set to true if all message loop invocations should exit and that the + // loop should be destroyed once it reaches the outermost Run invocation. + bool should_destroy_; + + // Since we allow tasks to be posted before the message loop is actually + // created (when it's associated with a thread), we keep tasks posted here + // until that happens. Once the loop_ is created, this is unused. + std::vector<TaskInfo> pending_tasks_; + + DISALLOW_COPY_AND_ASSIGN(MessageLoopResource); +}; + +MessageLoopResource::MessageLoopResource(PP_Instance instance) + : Resource(HostResource::MakeInstanceOnly(instance)), + nested_invocations_(0), + destroyed_(false), + should_destroy_(false) { +} + +MessageLoopResource::~MessageLoopResource() { +} + +PPB_MessageLoop_API* MessageLoopResource::AsPPB_MessageLoop_API() { + return this; +} + +int32_t MessageLoopResource::AttachToCurrentThread() { + if (tls_slot.initialized()) + return PP_ERROR_INPROGRESS; + // TODO(brettw) 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(); + tls_slot.Initialize(&ReleaseMessageLoop); + tls_slot.Set(this); + + loop_.reset(new MessageLoop(MessageLoop::TYPE_DEFAULT)); + + // 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; + // TODO(brettw) prevent this from happening on the main thread & return + // PP_ERROR_BLOCKS_MAIN_THREAD. Maybe have a special constructor for that + // one? + + // TODO(brettw) figure out how to release the lock. Can't run the message + // loop while holding the lock. + nested_invocations_++; + loop_->Run(); + nested_invocations_--; + + if (should_destroy_ && nested_invocations_ == 0) { + 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 (PP_ToBool(should_destroy)) + should_destroy_ = true; + + if (IsCurrent()) + loop_->Quit(); + else + PostClosure(FROM_HERE, MessageLoop::QuitClosure(), 0); + return PP_OK; +} + +void MessageLoopResource::DetachFromThread() { + // Note that the message loop must be destroyed on the thread is was created + // on. + loop_.reset(); + + // Cancel out the AddRef in AttachToCurrentThread(). + Release(); + // DANGER: may delete this. +} + +bool MessageLoopResource::IsCurrent() const { + if (!tls_slot.initialized()) + return false; // Can't be current if there's nothing in the slot. + return static_cast<const void*>(tls_slot.Get()) == + static_cast<const void*>(this); +} + +void MessageLoopResource::PostClosure( + const tracked_objects::Location& from_here, + const base::Closure& closure, + int64 delay_ms) { + if (loop_.get()) { + loop_->PostDelayedTask(from_here, closure, delay_ms); + } else { + TaskInfo info; + info.from_here = FROM_HERE; + info.closure = closure; + info.delay_ms = delay_ms; + pending_tasks_.push_back(info); + } +} + +// static +void MessageLoopResource::ReleaseMessageLoop(void* value) { + static_cast<MessageLoopResource*>(value)->DetachFromThread(); +} + +// ----------------------------------------------------------------------------- + +PP_Resource Create(PP_Instance instance) { + // Validate the instance. + PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance); + if (!dispatcher) + return 0; + return (new MessageLoopResource(instance))->GetReference(); +} + +PP_Resource GetForMainThread() { + // TODO(brettw). + return 0; +} + +PP_Resource GetCurrent() { + if (!tls_slot.initialized()) + return 0; + MessageLoopResource* loop = reinterpret_cast<MessageLoopResource*>( + tls_slot.Get()); + return loop->GetReference(); +} + +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_Dev_0_1 ppb_message_loop_interface = { + &Create, + &GetForMainThread, + &GetCurrent, + &AttachToCurrentThread, + &Run, + &PostWork, + &PostQuit +}; + +} // namespace + +PPB_MessageLoop_Proxy::PPB_MessageLoop_Proxy(Dispatcher* dispatcher) + : InterfaceProxy(dispatcher) { +} + +PPB_MessageLoop_Proxy::~PPB_MessageLoop_Proxy() { +} + +// static +const PPB_MessageLoop_Dev_0_1* PPB_MessageLoop_Proxy::GetInterface() { + return &ppb_message_loop_interface; +} + +} // namespace proxy +} // namespace ppapi diff --git a/ppapi/proxy/ppb_message_loop_proxy.h b/ppapi/proxy/ppb_message_loop_proxy.h new file mode 100644 index 0000000..aaf120a --- /dev/null +++ b/ppapi/proxy/ppb_message_loop_proxy.h @@ -0,0 +1,30 @@ +// 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. + +#ifndef PPAPI_PROXY_PPB_MESSAGE_LOOP_PROXY_H_ +#define PPAPI_PROXY_PPB_MESSAGE_LOOP_PROXY_H_ + +#include "base/basictypes.h" +#include "ppapi/proxy/interface_proxy.h" + +struct PPB_MessageLoop_Dev_0_1; + +namespace ppapi { +namespace proxy { + +class PPB_MessageLoop_Proxy : public InterfaceProxy { + public: + PPB_MessageLoop_Proxy(Dispatcher* dispatcher); + virtual ~PPB_MessageLoop_Proxy(); + + static const PPB_MessageLoop_Dev_0_1* GetInterface(); + + private: + DISALLOW_COPY_AND_ASSIGN(PPB_MessageLoop_Proxy); +}; + +} // namespace proxy +} // namespace ppapi + +#endif // PPAPI_PROXY_PPB_MESSAGE_LOOP_PROXY_H_ diff --git a/ppapi/shared_impl/resource.h b/ppapi/shared_impl/resource.h index c0efdb8..1c268b0 100644 --- a/ppapi/shared_impl/resource.h +++ b/ppapi/shared_impl/resource.h @@ -39,6 +39,7 @@ F(PPB_ImageData_API) \ F(PPB_InputEvent_API) \ F(PPB_LayerCompositor_API) \ + F(PPB_MessageLoop_API) \ F(PPB_PDFFont_API) \ F(PPB_ResourceArray_API) \ F(PPB_Scrollbar_API) \ diff --git a/ppapi/thunk/ppb_message_loop_api.h b/ppapi/thunk/ppb_message_loop_api.h new file mode 100644 index 0000000..36f2f1a --- /dev/null +++ b/ppapi/thunk/ppb_message_loop_api.h @@ -0,0 +1,25 @@ +// 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. + +#ifndef PPAPI_THUNK_PPB_MESSAGE_LOOP_API_H_ +#define PPAPI_THUNK_PPB_MESSAGE_LOOP_API_H_ + +namespace ppapi { +namespace thunk { + +class PPB_MessageLoop_API { + public: + virtual ~PPB_MessageLoop_API() {} + + virtual int32_t AttachToCurrentThread() = 0; + virtual int32_t Run() = 0; + virtual int32_t PostWork(PP_CompletionCallback callback, + int64_t delay_ms) = 0; + virtual int32_t PostQuit(PP_Bool should_destroy) = 0; +}; + +} // namespace thunk +} // namespace ppapi + +#endif // PPAPI_THUNK_PPB_MESSAGE_LOOP_API_H_ diff --git a/ppapi/utility/threading/simple_thread.cc b/ppapi/utility/threading/simple_thread.cc new file mode 100644 index 0000000..d0876e9 --- /dev/null +++ b/ppapi/utility/threading/simple_thread.cc @@ -0,0 +1,96 @@ +// 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/utility/threading/simple_thread.h" + +#ifdef WIN32 +#include <windows.h> +#endif + +namespace pp { + +namespace { + +struct ThreadData { + MessageLoop_Dev message_loop; + + SimpleThread::ThreadFunc func; + void* user_data; +}; + +#ifdef WIN32 +DWORD WINAPI RunThread(void* void_data) { +#else +void* RunThread(void* void_data) { +#endif + ThreadData* data = static_cast<ThreadData*>(void_data); + data->message_loop.AttachToCurrentThread(); + + if (data->func) + data->func(data->message_loop, data->user_data); + else + data->message_loop.Run(); + + delete data; + return NULL; +} + +} // namespace + +SimpleThread::SimpleThread(Instance* instance) + : instance_(instance), + message_loop_(instance), + thread_(0) { +} + +SimpleThread::~SimpleThread() { + Join(); +} + +bool SimpleThread::Start() { + return StartWithFunction(NULL, NULL); +} + +bool SimpleThread::Join() { + if (!thread_) + return false; + + message_loop_.PostQuit(true); + +#ifdef WIN32 + DWORD result = WaitForSingleObject(thread_, INFINITE); + CloseHandle(thread_); + thread_ = 0; + return result == WAIT_OBJECT_0; + +#else + void* retval; + int result = pthread_join(thread_, &retval); + thread_ = 0; + return result == 0; +#endif +} + +bool SimpleThread::StartWithFunction(ThreadFunc func, void* user_data) { + if (thread_) + return false; + + ThreadData* data = new ThreadData; + data->message_loop = message_loop_; + data->func = func; + data->user_data = user_data; + +#ifdef WIN32 + thread_ = CreateThread(NULL, 0, &RunThread, data, 0, NULL); + if (!thread_) { +#else + if (pthread_create(&thread_, NULL, &RunThread, data) != 0) { +#endif + delete data; + return false; + } + return true; +} + +} // namespace pp diff --git a/ppapi/utility/threading/simple_thread.h b/ppapi/utility/threading/simple_thread.h new file mode 100644 index 0000000..a093961 --- /dev/null +++ b/ppapi/utility/threading/simple_thread.h @@ -0,0 +1,65 @@ +// 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. + +#ifndef PPAPI_UTILITY_THREADING_SIMPLE_THREAD_H_ +#define PPAPI_UTILITY_THREADING_SIMPLE_THREAD_H_ + +#ifdef WIN32 +#include <windows.h> +#else +#include <pthread.h> +#endif + +#include "ppapi/cpp/dev/message_loop_dev.h" + +namespace pp { + +// This class is a simple wrapper around a pthread/Windows thread that creates +// and runs a PPAPI message loop on that thread. +class SimpleThread { + public: +#ifdef WIN32 + typedef HANDLE ThreadHandle; +#else + typedef pthread_t ThreadHandle; +#endif + + typedef void (*ThreadFunc)(MessageLoop_Dev&, void* user_data); + + SimpleThread(Instance* instance); + ~SimpleThread(); + + // Starts a thread and runs a message loop in it. If you need control over + // how the message loop is run, use StartWithFunction. Returns true on + // success, false if the thread is already running or couldn't be started. + bool Start(); + + // Posts a quit message to the message loop and blocks until the thread + // exits. Returns true on success. If the thread is not running, returns + // false. + bool Join(); + + // Normally you can just use Start() to start a thread, and then post work to + // it. In some cases you will want control over the message. If ThreadFunc + // is NULL, this acts the same as Start(). + bool StartWithFunction(ThreadFunc func, void* user_data); + + MessageLoop_Dev& message_loop() { return message_loop_; } + ThreadHandle thread() const { return thread_; } + + private: + Instance* instance_; + MessageLoop_Dev message_loop_; + + ThreadHandle thread_; + + // Disallow (not implemented). + SimpleThread(const SimpleThread&); + SimpleThread& operator=(const SimpleThread&); +}; + +} // namespace pp + +#endif // PPAPI_UTILITY_THREADING_SIMPLE_THREAD_H_ + |