diff options
author | yzshen@chromium.org <yzshen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-01-17 02:38:24 +0000 |
---|---|---|
committer | yzshen@chromium.org <yzshen@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-01-17 02:38:24 +0000 |
commit | 7ef6b79bd40c9e7ce84b05a7b8cc3ed28a0ddc72 (patch) | |
tree | 035507f07c0928c6ca8043fa7d01f64817bed3eb | |
parent | fd8609d8bd5255ab0a46b7169921a98de7837025 (diff) | |
download | chromium_src-7ef6b79bd40c9e7ce84b05a7b8cc3ed28a0ddc72.zip chromium_src-7ef6b79bd40c9e7ce84b05a7b8cc3ed28a0ddc72.tar.gz chromium_src-7ef6b79bd40c9e7ce84b05a7b8cc3ed28a0ddc72.tar.bz2 |
Pepper: Introduce ThreadAwareCallback.
Some PPB interfaces have methods that set a custom callback. Usually, the callback has to be called on the same thread as the one it was set on. ThreadAwareCallback keeps track of the target thread, and posts a task to run on it if requested from a different thread.
BUG=None
TEST=newly added unittests.
Review URL: https://chromiumcodereview.appspot.com/11859015
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@177319 0039d316-1c4b-4281-b951-d872f2087c98
21 files changed, 650 insertions, 68 deletions
diff --git a/chrome/browser/component_updater/test/component_installers_unittest.cc b/chrome/browser/component_updater/test/component_installers_unittest.cc index e7a4ee1..807f659 100644 --- a/chrome/browser/component_updater/test/component_installers_unittest.cc +++ b/chrome/browser/component_updater/test/component_installers_unittest.cc @@ -43,8 +43,8 @@ TEST(ComponentInstallerTest, PepperFlashCheck) { MessageLoop message_loop; content::TestBrowserThread ui_thread(BrowserThread::UI, &message_loop); - ppapi::PpapiGlobals::ForTest for_test; - ppapi::TestGlobals test_globals(for_test); + ppapi::PpapiGlobals::PerThreadForTest per_thread_for_test; + ppapi::TestGlobals test_globals(per_thread_for_test); ppapi::PpapiGlobals::SetPpapiGlobalsOnThreadForTest(&test_globals); // The test directory is chrome/test/data/components/flapper. diff --git a/ppapi/ppapi_shared.gypi b/ppapi/ppapi_shared.gypi index 67c8a02..ad03525 100644 --- a/ppapi/ppapi_shared.gypi +++ b/ppapi/ppapi_shared.gypi @@ -95,6 +95,8 @@ 'shared_impl/scoped_pp_resource.h', 'shared_impl/scoped_pp_var.cc', 'shared_impl/scoped_pp_var.h', + 'shared_impl/thread_aware_callback.cc', + 'shared_impl/thread_aware_callback.h', 'shared_impl/time_conversion.cc', 'shared_impl/time_conversion.h', 'shared_impl/tracked_callback.cc', diff --git a/ppapi/ppapi_tests.gypi b/ppapi/ppapi_tests.gypi index a229107..98ba36a 100644 --- a/ppapi/ppapi_tests.gypi +++ b/ppapi/ppapi_tests.gypi @@ -158,6 +158,7 @@ 'proxy/serialized_var_unittest.cc', 'proxy/websocket_resource_unittest.cc', 'shared_impl/resource_tracker_unittest.cc', + 'shared_impl/thread_aware_callback_unittest.cc', 'shared_impl/time_conversion_unittest.cc', 'shared_impl/tracked_callback_unittest.cc', 'shared_impl/var_tracker_unittest.cc', diff --git a/ppapi/proxy/device_enumeration_resource_helper.cc b/ppapi/proxy/device_enumeration_resource_helper.cc index 2b51c27..2729406 100644 --- a/ppapi/proxy/device_enumeration_resource_helper.cc +++ b/ppapi/proxy/device_enumeration_resource_helper.cc @@ -30,7 +30,6 @@ DeviceEnumerationResourceHelper::DeviceEnumerationResourceHelper( : owner_(owner), pending_enumerate_devices_(false), monitor_callback_id_(0), - monitor_callback_(NULL), monitor_user_data_(NULL) { } @@ -90,14 +89,19 @@ int32_t DeviceEnumerationResourceHelper::MonitorDeviceChange( PP_MonitorDeviceChangeCallback callback, void* user_data) { monitor_callback_id_++; - monitor_callback_ = callback; monitor_user_data_ = user_data; - if (callback) { + monitor_callback_.reset( + ThreadAwareCallback<PP_MonitorDeviceChangeCallback>::Create(callback)); + if (!monitor_callback_.get()) + return PP_ERROR_NO_MESSAGE_LOOP; + owner_->Post(PluginResource::RENDERER, PpapiHostMsg_DeviceEnumeration_MonitorDeviceChange( monitor_callback_id_)); } else { + monitor_callback_.reset(NULL); + owner_->Post(PluginResource::RENDERER, PpapiHostMsg_DeviceEnumeration_StopMonitoringDeviceChange()); } @@ -120,7 +124,7 @@ bool DeviceEnumerationResourceHelper::HandleReply( void DeviceEnumerationResourceHelper::LastPluginRefWasDeleted() { // Make sure that no further notifications are sent to the plugin. monitor_callback_id_++; - monitor_callback_ = NULL; + monitor_callback_.reset(NULL); monitor_user_data_ = NULL; // There is no need to do anything with pending callback of @@ -178,7 +182,7 @@ void DeviceEnumerationResourceHelper::OnPluginMsgNotifyDeviceChange( return; } - CHECK(monitor_callback_); + CHECK(monitor_callback_.get()); scoped_array<PP_Resource> elements; uint32_t size = devices.size(); @@ -191,10 +195,8 @@ void DeviceEnumerationResourceHelper::OnPluginMsgNotifyDeviceChange( } } - // TODO(yzshen): make sure |monitor_callback_| is called on the same thread as - // the one on which MonitorDeviceChange() is called. - CallWhileUnlocked(base::Bind(monitor_callback_, monitor_user_data_, size, - elements.get())); + monitor_callback_->RunOnTargetThread(monitor_user_data_, size, + elements.get()); for (size_t index = 0; index < size; ++index) PpapiGlobals::Get()->GetResourceTracker()->ReleaseResource(elements[index]); } diff --git a/ppapi/proxy/device_enumeration_resource_helper.h b/ppapi/proxy/device_enumeration_resource_helper.h index 6c266eb..a2574ec 100644 --- a/ppapi/proxy/device_enumeration_resource_helper.h +++ b/ppapi/proxy/device_enumeration_resource_helper.h @@ -12,6 +12,7 @@ #include "base/memory/weak_ptr.h" #include "ppapi/c/dev/ppb_device_ref_dev.h" #include "ppapi/proxy/ppapi_proxy_export.h" +#include "ppapi/shared_impl/thread_aware_callback.h" namespace IPC { class Message; @@ -74,7 +75,8 @@ class PPAPI_PROXY_EXPORT DeviceEnumerationResourceHelper bool pending_enumerate_devices_; uint32_t monitor_callback_id_; - PP_MonitorDeviceChangeCallback monitor_callback_; + scoped_ptr<ThreadAwareCallback<PP_MonitorDeviceChangeCallback> > + monitor_callback_; void* monitor_user_data_; DISALLOW_COPY_AND_ASSIGN(DeviceEnumerationResourceHelper); diff --git a/ppapi/proxy/plugin_globals.cc b/ppapi/proxy/plugin_globals.cc index 2913aa2..2e18ac5f 100644 --- a/ppapi/proxy/plugin_globals.cc +++ b/ppapi/proxy/plugin_globals.cc @@ -62,8 +62,8 @@ PluginGlobals::PluginGlobals() plugin_globals_ = this; } -PluginGlobals::PluginGlobals(ForTest for_test) - : ppapi::PpapiGlobals(for_test), +PluginGlobals::PluginGlobals(PerThreadForTest per_thread_for_test) + : ppapi::PpapiGlobals(per_thread_for_test), plugin_proxy_delegate_(NULL), callback_tracker_(new CallbackTracker) { #if defined(ENABLE_PEPPER_THREADING) diff --git a/ppapi/proxy/plugin_globals.h b/ppapi/proxy/plugin_globals.h index b323903..9b50295 100644 --- a/ppapi/proxy/plugin_globals.h +++ b/ppapi/proxy/plugin_globals.h @@ -30,7 +30,7 @@ class PluginProxyDelegate; class PPAPI_PROXY_EXPORT PluginGlobals : public PpapiGlobals { public: PluginGlobals(); - PluginGlobals(PpapiGlobals::ForTest); + explicit PluginGlobals(PpapiGlobals::PerThreadForTest); virtual ~PluginGlobals(); // Getter for the global singleton. Generally, you should use diff --git a/ppapi/proxy/ppapi_proxy_test.cc b/ppapi/proxy/ppapi_proxy_test.cc index 1af1dfc..1a475c0 100644 --- a/ppapi/proxy/ppapi_proxy_test.cc +++ b/ppapi/proxy/ppapi_proxy_test.cc @@ -7,14 +7,17 @@ #include <sstream> #include "base/bind.h" -#include "base/compiler_specific.h" +#include "base/bind_helpers.h" #include "base/message_loop_proxy.h" #include "base/observer_list.h" #include "base/process_util.h" +#include "base/run_loop.h" #include "ipc/ipc_sync_channel.h" #include "ppapi/c/pp_errors.h" #include "ppapi/c/private/ppb_proxy_private.h" #include "ppapi/proxy/ppapi_messages.h" +#include "ppapi/proxy/ppb_message_loop_proxy.h" +#include "ppapi/shared_impl/proxy_lock.h" namespace ppapi { namespace proxy { @@ -149,7 +152,9 @@ bool ProxyTestHarnessBase::SupportsInterface(const char* name) { // PluginProxyTestHarness ------------------------------------------------------ -PluginProxyTestHarness::PluginProxyTestHarness() { +PluginProxyTestHarness::PluginProxyTestHarness( + GlobalsConfiguration globals_config) + : globals_config_(globals_config) { } PluginProxyTestHarness::~PluginProxyTestHarness() { @@ -164,10 +169,9 @@ Dispatcher* PluginProxyTestHarness::GetDispatcher() { } void PluginProxyTestHarness::SetUpHarness() { - plugin_globals_.reset(new PluginGlobals(PpapiGlobals::ForTest())); - // These must be first since the dispatcher set-up uses them. - PpapiGlobals::SetPpapiGlobalsOnThreadForTest(GetGlobals()); + CreatePluginGlobals(); + resource_tracker().DidCreateInstance(pp_instance()); plugin_dispatcher_.reset(new PluginDispatcher( @@ -190,10 +194,9 @@ void PluginProxyTestHarness::SetUpHarnessWithChannel( base::MessageLoopProxy* ipc_message_loop, base::WaitableEvent* shutdown_event, bool is_client) { - plugin_globals_.reset(new PluginGlobals(PpapiGlobals::ForTest())); - // These must be first since the dispatcher set-up uses them. - PpapiGlobals::SetPpapiGlobalsOnThreadForTest(GetGlobals()); + CreatePluginGlobals(); + resource_tracker().DidCreateInstance(pp_instance()); plugin_delegate_mock_.Init(ipc_message_loop, shutdown_event); @@ -218,6 +221,15 @@ void PluginProxyTestHarness::TearDownHarness() { plugin_globals_.reset(); } +void PluginProxyTestHarness::CreatePluginGlobals() { + if (globals_config_ == PER_THREAD_GLOBALS) { + plugin_globals_.reset(new PluginGlobals(PpapiGlobals::PerThreadForTest())); + PpapiGlobals::SetPpapiGlobalsOnThreadForTest(GetGlobals()); + } else { + plugin_globals_.reset(new PluginGlobals()); + } +} + base::MessageLoopProxy* PluginProxyTestHarness::PluginDelegateMock::GetIPCMessageLoop() { return ipc_message_loop_; @@ -270,7 +282,7 @@ void PluginProxyTestHarness::PluginDelegateMock::SetActiveURL( // PluginProxyTest ------------------------------------------------------------- -PluginProxyTest::PluginProxyTest() { +PluginProxyTest::PluginProxyTest() : PluginProxyTestHarness(SINGLETON_GLOBALS) { } PluginProxyTest::~PluginProxyTest() { @@ -284,6 +296,102 @@ void PluginProxyTest::TearDown() { TearDownHarness(); } +// PluginProxyMultiThreadTest -------------------------------------------------- + +PluginProxyMultiThreadTest::PluginProxyMultiThreadTest() { +} + +PluginProxyMultiThreadTest::~PluginProxyMultiThreadTest() { +} + +void PluginProxyMultiThreadTest::RunTest() { + main_thread_message_loop_proxy_ = + PpapiGlobals::Get()->GetMainThreadMessageLoop(); + ASSERT_EQ(main_thread_message_loop_proxy_.get(), + base::MessageLoopProxy::current()); + nested_main_thread_message_loop_.reset(new base::RunLoop()); + + secondary_thread_.reset(new base::DelegateSimpleThread( + this, "PluginProxyMultiThreadTest")); + + { + ProxyAutoLock auto_lock; + + // MessageLoopResource assumes that the proxy lock has been acquired. + secondary_thread_message_loop_ = new MessageLoopResource(pp_instance()); + + // TODO(yzshen): The comment of PPB_MessageLoop says that it would return + // PP_OK_COMPLETIONPENDING. Either fix the comment or the implementation. + ASSERT_EQ(PP_OK, + secondary_thread_message_loop_->PostWork( + PP_MakeCompletionCallback( + &PluginProxyMultiThreadTest::InternalSetUpTestOnSecondaryThread, + this), + 0)); + } + + SetUpTestOnMainThread(); + + secondary_thread_->Start(); + nested_main_thread_message_loop_->Run(); + secondary_thread_->Join(); + + { + ProxyAutoLock auto_lock; + + // The destruction requires a valid PpapiGlobals instance, so we should + // explicitly release it. + secondary_thread_message_loop_ = NULL; + } + + secondary_thread_.reset(NULL); + nested_main_thread_message_loop_.reset(NULL); + main_thread_message_loop_proxy_ = NULL; +} + +void PluginProxyMultiThreadTest::CheckOnThread(ThreadType thread_type) { + ProxyAutoLock auto_lock; + if (thread_type == MAIN_THREAD) { + ASSERT_TRUE(MessageLoopResource::GetCurrent()->is_main_thread_loop()); + } else { + ASSERT_EQ(secondary_thread_message_loop_.get(), + MessageLoopResource::GetCurrent()); + } +} + +void PluginProxyMultiThreadTest::PostQuitForMainThread() { + main_thread_message_loop_proxy_->PostTask( + FROM_HERE, + base::Bind(&PluginProxyMultiThreadTest::QuitNestedLoop, + base::Unretained(this))); +} + +void PluginProxyMultiThreadTest::PostQuitForSecondaryThread() { + ProxyAutoLock auto_lock; + secondary_thread_message_loop_->PostQuit(PP_TRUE); +} + +void PluginProxyMultiThreadTest::Run() { + ProxyAutoLock auto_lock; + ASSERT_EQ(PP_OK, secondary_thread_message_loop_->AttachToCurrentThread()); + ASSERT_EQ(PP_OK, secondary_thread_message_loop_->Run()); +} + +void PluginProxyMultiThreadTest::QuitNestedLoop() { + nested_main_thread_message_loop_->Quit(); +} + +// static +void PluginProxyMultiThreadTest::InternalSetUpTestOnSecondaryThread( + void* user_data, + int32_t result) { + EXPECT_EQ(PP_OK, result); + PluginProxyMultiThreadTest* thiz = + static_cast<PluginProxyMultiThreadTest*>(user_data); + thiz->CheckOnThread(SECONDARY_THREAD); + thiz->SetUpTestOnSecondaryThread(); +} + // HostProxyTestHarness -------------------------------------------------------- class HostProxyTestHarness::MockSyncMessageStatusReceiver @@ -293,8 +401,9 @@ class HostProxyTestHarness::MockSyncMessageStatusReceiver virtual void EndBlockOnSyncMessage() OVERRIDE {} }; -HostProxyTestHarness::HostProxyTestHarness() - : status_receiver_(new MockSyncMessageStatusReceiver) { +HostProxyTestHarness::HostProxyTestHarness(GlobalsConfiguration globals_config) + : globals_config_(globals_config), + status_receiver_(new MockSyncMessageStatusReceiver) { } HostProxyTestHarness::~HostProxyTestHarness() { @@ -309,10 +418,9 @@ Dispatcher* HostProxyTestHarness::GetDispatcher() { } void HostProxyTestHarness::SetUpHarness() { - host_globals_.reset(new ppapi::TestGlobals(PpapiGlobals::ForTest())); - // These must be first since the dispatcher set-up uses them. - PpapiGlobals::SetPpapiGlobalsOnThreadForTest(GetGlobals()); + CreateHostGlobals(); + host_dispatcher_.reset(new HostDispatcher( pp_module(), &MockGetInterface, @@ -327,10 +435,9 @@ void HostProxyTestHarness::SetUpHarnessWithChannel( base::MessageLoopProxy* ipc_message_loop, base::WaitableEvent* shutdown_event, bool is_client) { - host_globals_.reset(new ppapi::TestGlobals(PpapiGlobals::ForTest())); - // These must be first since the dispatcher set-up uses them. - PpapiGlobals::SetPpapiGlobalsOnThreadForTest(GetGlobals()); + CreateHostGlobals(); + delegate_mock_.Init(ipc_message_loop, shutdown_event); host_dispatcher_.reset(new HostDispatcher( @@ -351,6 +458,15 @@ void HostProxyTestHarness::TearDownHarness() { host_globals_.reset(); } +void HostProxyTestHarness::CreateHostGlobals() { + if (globals_config_ == PER_THREAD_GLOBALS) { + host_globals_.reset(new TestGlobals(PpapiGlobals::PerThreadForTest())); + PpapiGlobals::SetPpapiGlobalsOnThreadForTest(GetGlobals()); + } else { + host_globals_.reset(new TestGlobals()); + } +} + base::MessageLoopProxy* HostProxyTestHarness::DelegateMock::GetIPCMessageLoop() { return ipc_message_loop_; @@ -373,7 +489,7 @@ HostProxyTestHarness::DelegateMock::ShareHandleWithRemote( // HostProxyTest --------------------------------------------------------------- -HostProxyTest::HostProxyTest() { +HostProxyTest::HostProxyTest() : HostProxyTestHarness(SINGLETON_GLOBALS) { } HostProxyTest::~HostProxyTest() { @@ -391,6 +507,8 @@ void HostProxyTest::TearDown() { TwoWayTest::TwoWayTest(TwoWayTest::TwoWayTestMode test_mode) : test_mode_(test_mode), + host_(ProxyTestHarnessBase::PER_THREAD_GLOBALS), + plugin_(ProxyTestHarnessBase::PER_THREAD_GLOBALS), io_thread_("TwoWayTest_IOThread"), plugin_thread_("TwoWayTest_PluginThread"), remote_harness_(NULL), diff --git a/ppapi/proxy/ppapi_proxy_test.h b/ppapi/proxy/ppapi_proxy_test.h index e8c15c1..0882790 100644 --- a/ppapi/proxy/ppapi_proxy_test.h +++ b/ppapi/proxy/ppapi_proxy_test.h @@ -5,9 +5,12 @@ #include <map> #include <string> -#include "base/message_loop.h" +#include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" +#include "base/message_loop.h" #include "base/synchronization/waitable_event.h" +#include "base/threading/simple_thread.h" #include "base/threading/thread.h" #include "ppapi/c/pp_instance.h" #include "ppapi/proxy/host_dispatcher.h" @@ -20,13 +23,25 @@ #include "ppapi/shared_impl/test_globals.h" #include "testing/gtest/include/gtest/gtest.h" +namespace base { +class MessageLoopProxy; +class RunLoop; +} + namespace ppapi { namespace proxy { +class MessageLoopResource; + // Base class for plugin and host test harnesses. Tests will not use this // directly. Instead, use the PluginProxyTest, HostProxyTest, or TwoWayTest. class ProxyTestHarnessBase { public: + enum GlobalsConfiguration { + PER_THREAD_GLOBALS, + SINGLETON_GLOBALS + }; + ProxyTestHarnessBase(); virtual ~ProxyTestHarnessBase(); @@ -78,7 +93,7 @@ class ProxyTestHarnessBase { // Test harness for the plugin side of the proxy. class PluginProxyTestHarness : public ProxyTestHarnessBase { public: - PluginProxyTestHarness(); + explicit PluginProxyTestHarness(GlobalsConfiguration globals_config); virtual ~PluginProxyTestHarness(); PluginDispatcher* plugin_dispatcher() { return plugin_dispatcher_.get(); } @@ -144,6 +159,9 @@ class PluginProxyTestHarness : public ProxyTestHarnessBase { }; private: + void CreatePluginGlobals(); + + GlobalsConfiguration globals_config_; scoped_ptr<PluginGlobals> plugin_globals_; scoped_ptr<PluginDispatcher> plugin_dispatcher_; @@ -162,9 +180,58 @@ class PluginProxyTest : public PluginProxyTestHarness, public testing::Test { MessageLoop message_loop_; }; +// This class provides support for multi-thread testing. A secondary thread is +// created with a Pepper message loop. +// Subclasses need to implement the two SetUpTestOn*Thread() methods to do the +// actual testing work; and call both PostQuitFor*Thread() when testing is +// done. +class PluginProxyMultiThreadTest + : public PluginProxyTest, + public base::DelegateSimpleThread::Delegate { + public: + PluginProxyMultiThreadTest(); + virtual ~PluginProxyMultiThreadTest(); + + // Called before the secondary thread is started, but after all the member + // variables, including |secondary_thread_| and + // |secondary_thread_message_loop_|, are initialized. + virtual void SetUpTestOnMainThread() = 0; + + virtual void SetUpTestOnSecondaryThread() = 0; + + // TEST_F() should call this method. + void RunTest(); + + enum ThreadType { + MAIN_THREAD, + SECONDARY_THREAD + }; + void CheckOnThread(ThreadType thread_type); + + // These can be called on any thread. + void PostQuitForMainThread(); + void PostQuitForSecondaryThread(); + + protected: + scoped_refptr<MessageLoopResource> secondary_thread_message_loop_; + scoped_refptr<base::MessageLoopProxy> main_thread_message_loop_proxy_; + + private: + // base::DelegateSimpleThread::Delegate implementation. + virtual void Run() OVERRIDE; + + void QuitNestedLoop(); + + static void InternalSetUpTestOnSecondaryThread(void* user_data, + int32_t result); + + scoped_ptr<base::DelegateSimpleThread> secondary_thread_; + scoped_ptr<base::RunLoop> nested_main_thread_message_loop_; +}; + class HostProxyTestHarness : public ProxyTestHarnessBase { public: - HostProxyTestHarness(); + explicit HostProxyTestHarness(GlobalsConfiguration globals_config); virtual ~HostProxyTestHarness(); HostDispatcher* host_dispatcher() { return host_dispatcher_.get(); } @@ -215,6 +282,9 @@ class HostProxyTestHarness : public ProxyTestHarnessBase { private: class MockSyncMessageStatusReceiver; + void CreateHostGlobals(); + + GlobalsConfiguration globals_config_; scoped_ptr<ppapi::TestGlobals> host_globals_; scoped_ptr<HostDispatcher> host_dispatcher_; DelegateMock delegate_mock_; diff --git a/ppapi/proxy/ppb_message_loop_proxy.h b/ppapi/proxy/ppb_message_loop_proxy.h index 5d161c5..3fa7674 100644 --- a/ppapi/proxy/ppb_message_loop_proxy.h +++ b/ppapi/proxy/ppb_message_loop_proxy.h @@ -11,6 +11,7 @@ #include "base/memory/scoped_ptr.h" #include "base/message_loop.h" #include "ppapi/proxy/interface_proxy.h" +#include "ppapi/proxy/ppapi_proxy_export.h" #include "ppapi/shared_impl/ppb_message_loop_shared.h" #include "ppapi/thunk/ppb_message_loop_api.h" @@ -19,7 +20,7 @@ struct PPB_MessageLoop_1_0; namespace ppapi { namespace proxy { -class MessageLoopResource : public MessageLoopShared { +class PPAPI_PROXY_EXPORT MessageLoopResource : public MessageLoopShared { public: explicit MessageLoopResource(PP_Instance instance); // Construct the one MessageLoopResource for the main thread. This must be diff --git a/ppapi/proxy/ppb_var_unittest.cc b/ppapi/proxy/ppb_var_unittest.cc index 74ed453..bf6147d 100644 --- a/ppapi/proxy/ppb_var_unittest.cc +++ b/ppapi/proxy/ppb_var_unittest.cc @@ -10,7 +10,6 @@ #include "ppapi/c/pp_var.h" #include "ppapi/c/ppb_var.h" #include "ppapi/proxy/ppapi_proxy_test.h" -#include "ppapi/shared_impl/ppapi_globals.h" #include "ppapi/shared_impl/ppb_var_shared.h" namespace { @@ -97,13 +96,12 @@ class CreateVarThreadDelegate : public base::PlatformThread::Delegate { // read the var back out to |strings_out[i]|. CreateVarThreadDelegate(PP_Module pp_module, const std::string* strings_in, PP_Var* vars_out, std::string* strings_out, - size_t size, PpapiGlobals* globals) + size_t size) : pp_module_(pp_module), strings_in_(strings_in), vars_out_(vars_out), - strings_out_(strings_out), size_(size), globals_(globals) { + strings_out_(strings_out), size_(size) { } virtual ~CreateVarThreadDelegate() {} virtual void ThreadMain() { - PpapiGlobals::SetPpapiGlobalsOnThreadForTest(globals_); const PPB_Var* ppb_var = ppapi::PPB_Var_Shared::GetVarInterface1_1(); for (size_t i = 0; i < size_; ++i) { vars_out_[i] = ppb_var->VarFromUtf8(strings_in_[i].c_str(), @@ -117,20 +115,16 @@ class CreateVarThreadDelegate : public base::PlatformThread::Delegate { PP_Var* vars_out_; std::string* strings_out_; size_t size_; - PpapiGlobals* globals_; }; // A thread that will increment and decrement the reference count of every var // multiple times. class ChangeRefVarThreadDelegate : public base::PlatformThread::Delegate { public: - ChangeRefVarThreadDelegate(const std::vector<PP_Var>& vars, - PpapiGlobals* globals) - : vars_(vars), globals_(globals) { + ChangeRefVarThreadDelegate(const std::vector<PP_Var>& vars) : vars_(vars) { } virtual ~ChangeRefVarThreadDelegate() {} virtual void ThreadMain() { - PpapiGlobals::SetPpapiGlobalsOnThreadForTest(globals_); const PPB_Var* ppb_var = ppapi::PPB_Var_Shared::GetVarInterface1_1(); // Increment and decrement the reference count for each var kRefsToAdd // times. Note that we always AddRef once before doing the matching Release, @@ -150,19 +144,15 @@ class ChangeRefVarThreadDelegate : public base::PlatformThread::Delegate { } private: std::vector<PP_Var> vars_; - PpapiGlobals* globals_; }; // A thread that will decrement the reference count of every var once. class RemoveRefVarThreadDelegate : public base::PlatformThread::Delegate { public: - RemoveRefVarThreadDelegate(const std::vector<PP_Var>& vars, - PpapiGlobals* globals) - : vars_(vars), globals_(globals) { + RemoveRefVarThreadDelegate(const std::vector<PP_Var>& vars) : vars_(vars) { } virtual ~RemoveRefVarThreadDelegate() {} virtual void ThreadMain() { - PpapiGlobals::SetPpapiGlobalsOnThreadForTest(globals_); const PPB_Var* ppb_var = ppapi::PPB_Var_Shared::GetVarInterface1_1(); for (size_t i = 0; i < kNumStrings; ++i) { ppb_var->Release(vars_[i]); @@ -170,7 +160,6 @@ class RemoveRefVarThreadDelegate : public base::PlatformThread::Delegate { } private: std::vector<PP_Var> vars_; - PpapiGlobals* globals_; }; } // namespace @@ -197,8 +186,7 @@ TEST_F(PPB_VarTest, DISABLED_Threads) { &vars_[slice_start], &strings_out[slice_start], std::min(strings_per_thread, - kNumStrings - slice_start), - GetGlobals())); + kNumStrings - slice_start))); } // Now run then join all the threads. for (size_t i = 0; i < kNumThreads; ++i) @@ -213,8 +201,7 @@ TEST_F(PPB_VarTest, DISABLED_Threads) { std::vector<base::PlatformThreadHandle> change_ref_var_threads(kNumThreads); std::vector<ChangeRefVarThreadDelegate> change_ref_var_delegates; for (size_t i = 0; i < kNumThreads; ++i) - change_ref_var_delegates.push_back( - ChangeRefVarThreadDelegate(vars_, GetGlobals())); + change_ref_var_delegates.push_back(ChangeRefVarThreadDelegate(vars_)); for (size_t i = 0; i < kNumThreads; ++i) { base::PlatformThread::Create(0, &change_ref_var_delegates[i], &change_ref_var_threads[i]); @@ -237,8 +224,7 @@ TEST_F(PPB_VarTest, DISABLED_Threads) { std::vector<base::PlatformThreadHandle> remove_ref_var_threads(kNumThreads); std::vector<RemoveRefVarThreadDelegate> remove_ref_var_delegates; for (size_t i = 0; i < kNumThreads; ++i) - remove_ref_var_delegates.push_back( - RemoveRefVarThreadDelegate(vars_, GetGlobals())); + remove_ref_var_delegates.push_back(RemoveRefVarThreadDelegate(vars_)); for (size_t i = 0; i < kNumThreads; ++i) { base::PlatformThread::Create(0, &remove_ref_var_delegates[i], &remove_ref_var_threads[i]); diff --git a/ppapi/shared_impl/DEPS b/ppapi/shared_impl/DEPS index 823b983..9f93141 100644 --- a/ppapi/shared_impl/DEPS +++ b/ppapi/shared_impl/DEPS @@ -14,4 +14,7 @@ include_rules = [ "-ppapi/cpp", "-ppapi/proxy", + + # For testing purpose. + "+ppapi/proxy/ppapi_proxy_test.h", ] diff --git a/ppapi/shared_impl/ppapi_globals.cc b/ppapi/shared_impl/ppapi_globals.cc index 270d7e8..be10d4c 100644 --- a/ppapi/shared_impl/ppapi_globals.cc +++ b/ppapi/shared_impl/ppapi_globals.cc @@ -26,7 +26,7 @@ PpapiGlobals::PpapiGlobals() { main_loop_proxy_ = base::MessageLoopProxy::current(); } -PpapiGlobals::PpapiGlobals(ForTest) { +PpapiGlobals::PpapiGlobals(PerThreadForTest) { DCHECK(!ppapi_globals_); main_loop_proxy_ = base::MessageLoopProxy::current(); } diff --git a/ppapi/shared_impl/ppapi_globals.h b/ppapi/shared_impl/ppapi_globals.h index 0573c98..84b90e7 100644 --- a/ppapi/shared_impl/ppapi_globals.h +++ b/ppapi/shared_impl/ppapi_globals.h @@ -43,8 +43,8 @@ class PPAPI_SHARED_EXPORT PpapiGlobals { // purposes. This avoids setting the global static ppapi_globals_. For unit // tests that use this feature, the "test" PpapiGlobals should be constructed // using this method. See SetPpapiGlobalsOnThreadForTest for more information. - struct ForTest {}; - explicit PpapiGlobals(ForTest); + struct PerThreadForTest {}; + explicit PpapiGlobals(PerThreadForTest); virtual ~PpapiGlobals(); @@ -64,7 +64,7 @@ class PPAPI_SHARED_EXPORT PpapiGlobals { // the same process, e.g. for having 1 thread emulate the "host" and 1 thread // emulate the "plugin". // - // PpapiGlobals object must have been constructed using the "ForTest" + // PpapiGlobals object must have been constructed using the "PerThreadForTest" // parameter. static void SetPpapiGlobalsOnThreadForTest(PpapiGlobals* ptr); diff --git a/ppapi/shared_impl/test_globals.cc b/ppapi/shared_impl/test_globals.cc index cf19286..913d53c 100644 --- a/ppapi/shared_impl/test_globals.cc +++ b/ppapi/shared_impl/test_globals.cc @@ -11,8 +11,8 @@ TestGlobals::TestGlobals() callback_tracker_(new CallbackTracker) { } -TestGlobals::TestGlobals(PpapiGlobals::ForTest for_test) - : ppapi::PpapiGlobals(for_test), +TestGlobals::TestGlobals(PpapiGlobals::PerThreadForTest per_thread_for_test) + : ppapi::PpapiGlobals(per_thread_for_test), callback_tracker_(new CallbackTracker) { } diff --git a/ppapi/shared_impl/test_globals.h b/ppapi/shared_impl/test_globals.h index ea23ec7..68a997a 100644 --- a/ppapi/shared_impl/test_globals.h +++ b/ppapi/shared_impl/test_globals.h @@ -29,7 +29,7 @@ class TestVarTracker : public VarTracker { class TestGlobals : public PpapiGlobals { public: TestGlobals(); - TestGlobals(PpapiGlobals::ForTest); + explicit TestGlobals(PpapiGlobals::PerThreadForTest); virtual ~TestGlobals(); // PpapiGlobals implementation. diff --git a/ppapi/shared_impl/thread_aware_callback.cc b/ppapi/shared_impl/thread_aware_callback.cc new file mode 100644 index 0000000..9b1063b --- /dev/null +++ b/ppapi/shared_impl/thread_aware_callback.cc @@ -0,0 +1,63 @@ +// 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/shared_impl/thread_aware_callback.h" + +#include "base/callback.h" +#include "base/logging.h" +#include "ppapi/shared_impl/ppapi_globals.h" +#include "ppapi/shared_impl/ppb_message_loop_shared.h" + +namespace ppapi { +namespace internal { + +class ThreadAwareCallbackBase::Core : public base::RefCountedThreadSafe<Core> { + public: + Core() : aborted_(false) { + } + + void MarkAsAborted() { aborted_ = true; } + + void RunIfNotAborted(const base::Closure& closure) { + if (!aborted_) + CallWhileUnlocked(closure); + } + + private: + friend class base::RefCountedThreadSafe<Core>; + ~Core() { + } + + bool aborted_; +}; + +ThreadAwareCallbackBase::ThreadAwareCallbackBase() + : target_loop_(PpapiGlobals::Get()->GetCurrentMessageLoop()), + core_(new Core()) { + DCHECK(target_loop_.get()); +} + +ThreadAwareCallbackBase::~ThreadAwareCallbackBase() { + core_->MarkAsAborted(); +} + +// static +bool ThreadAwareCallbackBase::HasTargetLoop() { + return !!PpapiGlobals::Get()->GetCurrentMessageLoop(); +} + +void ThreadAwareCallbackBase::InternalRunOnTargetThread( + const base::Closure& closure) { + if (target_loop_ != PpapiGlobals::Get()->GetCurrentMessageLoop()) { + target_loop_->PostClosure( + FROM_HERE, + RunWhileLocked(base::Bind(&Core::RunIfNotAborted, core_, closure)), + 0); + } else { + CallWhileUnlocked(closure); + } +} + +} // namespace internal +} // namespace ppapi diff --git a/ppapi/shared_impl/thread_aware_callback.h b/ppapi/shared_impl/thread_aware_callback.h new file mode 100644 index 0000000..b954eec --- /dev/null +++ b/ppapi/shared_impl/thread_aware_callback.h @@ -0,0 +1,115 @@ +// Copyright (c) 2013 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_SHARED_IMPL_THREAD_AWARE_CALLBACK_H_ +#define PPAPI_SHARED_IMPL_THREAD_AWARE_CALLBACK_H_ + +#include "base/basictypes.h" +#include "base/bind.h" +#include "base/memory/ref_counted.h" +#include "ppapi/shared_impl/ppapi_shared_export.h" +#include "ppapi/shared_impl/proxy_lock.h" + +namespace ppapi { + +class MessageLoopShared; + +namespace internal { + +class PPAPI_SHARED_EXPORT ThreadAwareCallbackBase { + protected: + ThreadAwareCallbackBase(); + ~ThreadAwareCallbackBase(); + + static bool HasTargetLoop(); + + void InternalRunOnTargetThread(const base::Closure& closure); + + private: + class Core; + + scoped_refptr<MessageLoopShared> target_loop_; + scoped_refptr<Core> core_; + + DISALLOW_COPY_AND_ASSIGN(ThreadAwareCallbackBase); +}; + +} // namespace internal + +// Some PPB interfaces have methods that set a custom callback. Usually, the +// callback has to be called on the same thread as the one it was set on. +// ThreadAwareCallback keeps track of the target thread, and posts a task to run +// on it if requested from a different thread. +// +// Please note that: +// - Unlike TrackedCallback, there is no restriction on how many times the +// callback will be called. +// - When a ThreadAwareCallback object is destroyed, all pending tasks to run +// the callback will be ignored. It is designed this way so that when the +// resource is destroyed or the callback is cancelled by the plugin, we can +// simply delete the ThreadAwareCallback object to prevent touching the +// callback later. +// - When RunOnTargetThread() is called on the target thread, the callback runs +// immediately. +template <class FuncType> +class ThreadAwareCallback : public internal::ThreadAwareCallbackBase { + public: + // The caller takes ownership of the returned object. + // NULL is returned if the current thread doesn't have an associated Pepper + // message loop, or |func| is NULL. + static ThreadAwareCallback* Create(FuncType func) { + if (!func || !HasTargetLoop()) + return NULL; + return new ThreadAwareCallback(func); + } + + ~ThreadAwareCallback() { + } + + void RunOnTargetThread() { + InternalRunOnTargetThread(base::Bind(func_)); + } + + template <class P1> + void RunOnTargetThread(const P1& p1) { + InternalRunOnTargetThread(base::Bind(func_, p1)); + } + + template <class P1, class P2> + void RunOnTargetThread(const P1& p1, const P2& p2) { + InternalRunOnTargetThread(base::Bind(func_, p1, p2)); + } + + template <class P1, class P2, class P3> + void RunOnTargetThread(const P1& p1, const P2& p2, const P3& p3) { + InternalRunOnTargetThread(base::Bind(func_, p1, p2, p3)); + } + + template <class P1, class P2, class P3, class P4> + void RunOnTargetThread(const P1& p1, + const P2& p2, + const P3& p3, + const P4& p4) { + InternalRunOnTargetThread(base::Bind(func_, p1, p2, p3, p4)); + } + + template <class P1, class P2, class P3, class P4, class P5> + void RunOnTargetThread(const P1& p1, + const P2& p2, + const P3& p3, + const P4& p4, + const P5& p5) { + InternalRunOnTargetThread(base::Bind(func_, p1, p2, p3, p4, p5)); + } + + private: + explicit ThreadAwareCallback(FuncType func) : func_(func) { + } + + FuncType func_; +}; + +} // namespace ppapi + +#endif // PPAPI_SHARED_IMPL_THREAD_AWARE_CALLBACK_H_ diff --git a/ppapi/shared_impl/thread_aware_callback_unittest.cc b/ppapi/shared_impl/thread_aware_callback_unittest.cc new file mode 100644 index 0000000..f86048a7 --- /dev/null +++ b/ppapi/shared_impl/thread_aware_callback_unittest.cc @@ -0,0 +1,218 @@ +// 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/shared_impl/thread_aware_callback.h" + +#include "base/bind_helpers.h" +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "ppapi/c/pp_errors.h" +#include "ppapi/proxy/ppapi_proxy_test.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace ppapi { + +namespace { + +class TestParameter { + public: + TestParameter() : value_(0) { + } + + int value_; +}; + +int called_num = 0; + +void TestCallback_0() { + ++called_num; +} + +void TestCallback_1(int p1) { + ++called_num; +} + +void TestCallback_2(int p1, const double* p2) { + ++called_num; +} + +void TestCallback_3(int p1, const double* p2, bool* p3) { + ++called_num; +} + +void TestCallback_4(int p1, const double* p2, bool* p3, TestParameter p4) { + ++called_num; +} + +void TestCallback_5(int p1, + const double* p2, + bool* p3, + TestParameter p4, + const TestParameter& p5) { + ++called_num; +} + +typedef proxy::PluginProxyTest ThreadAwareCallbackTest; + +// Test that a callback created on the main thread will run on the main thread, +// even when requested from a different thread. +class ThreadAwareCallbackMultiThreadTest + : public proxy::PluginProxyMultiThreadTest { + public: + ThreadAwareCallbackMultiThreadTest() : main_thread_callback_called_(false) { + } + virtual ~ThreadAwareCallbackMultiThreadTest() { + CHECK(main_thread_callback_called_); + } + + // proxy::PluginProxyMultiThreadTest implementation. + virtual void SetUpTestOnMainThread() OVERRIDE { + ProxyAutoLock auto_lock; + + main_thread_callback_.reset( + ThreadAwareCallback<CallbackFunc>::Create(&MainThreadCallbackBody)); + } + + virtual void SetUpTestOnSecondaryThread() OVERRIDE { + { + ProxyAutoLock auto_lock; + main_thread_callback_->RunOnTargetThread(this); + } + + PostQuitForSecondaryThread(); + PostQuitForMainThread(); + } + + private: + typedef void (*CallbackFunc)(ThreadAwareCallbackMultiThreadTest*); + + static void MainThreadCallbackBody(ThreadAwareCallbackMultiThreadTest* thiz) { + thiz->CheckOnThread(MAIN_THREAD); + thiz->main_thread_callback_called_ = true; + + { + ProxyAutoLock auto_lock; + // We have to destroy it prior to the PluginGlobals instance held by the + // base class. Otherwise it has a ref to Pepper message loop for the main + // thread and the PluginGlobals destructor will complain. + thiz->main_thread_callback_.reset(NULL); + } + } + + scoped_ptr<ThreadAwareCallback<CallbackFunc> > main_thread_callback_; + bool main_thread_callback_called_; +}; + +// Test that when a ThreadAwareCallback instance is destroyed, pending tasks to +// run the callback will be ignored. +class ThreadAwareCallbackAbortTest : public proxy::PluginProxyMultiThreadTest { + public: + ThreadAwareCallbackAbortTest() { + } + virtual ~ThreadAwareCallbackAbortTest() { + } + + // proxy::PluginProxyMultiThreadTest implementation. + virtual void SetUpTestOnMainThread() OVERRIDE { + ProxyAutoLock auto_lock; + + main_thread_callback_.reset( + ThreadAwareCallback<CallbackFunc>::Create(&MainThreadCallbackBody)); + } + + virtual void SetUpTestOnSecondaryThread() OVERRIDE { + { + ProxyAutoLock auto_lock; + main_thread_message_loop_proxy_->PostTask( + FROM_HERE, + base::Bind(&ThreadAwareCallbackAbortTest::DeleteCallback, + base::Unretained(this))); + // |main_thread_callback_| is still valid, even if DeleteCallback() can be + // called before this following statement. That is because |auto_lock| is + // still held by this method, which prevents DeleteCallback() from + // deleting the callback. + main_thread_callback_->RunOnTargetThread(this); + } + + PostQuitForSecondaryThread(); + PostQuitForMainThread(); + } + + private: + typedef void (*CallbackFunc)(ThreadAwareCallbackAbortTest*); + + static void MainThreadCallbackBody(ThreadAwareCallbackAbortTest* thiz) { + // The callback should not be called. + ASSERT_TRUE(false); + } + + void DeleteCallback() { + ProxyAutoLock auto_lock; + main_thread_callback_.reset(NULL); + } + + scoped_ptr<ThreadAwareCallback<CallbackFunc> > main_thread_callback_; +}; + +} // namespace + +TEST_F(ThreadAwareCallbackTest, Basics) { + // ThreadAwareCallback should only be used when the proxy lock has been + // acquired. + ProxyAutoLock auto_lock; + + double double_arg = 0.0; + bool bool_arg = false; + TestParameter object_arg; + + // Exercise all the template code. + called_num = 0; + typedef void (*FuncType_0)(); + scoped_ptr<ThreadAwareCallback<FuncType_0> > callback_0( + ThreadAwareCallback<FuncType_0>::Create(TestCallback_0)); + callback_0->RunOnTargetThread(); + + typedef void (*FuncType_1)(int); + scoped_ptr<ThreadAwareCallback<FuncType_1> > callback_1( + ThreadAwareCallback<FuncType_1>::Create(TestCallback_1)); + callback_1->RunOnTargetThread(1); + + typedef void (*FuncType_2)(int, const double*); + scoped_ptr<ThreadAwareCallback<FuncType_2> > callback_2( + ThreadAwareCallback<FuncType_2>::Create(TestCallback_2)); + callback_2->RunOnTargetThread(1, &double_arg); + + typedef void (*FuncType_3)(int, const double*, bool*); + scoped_ptr<ThreadAwareCallback<FuncType_3> > callback_3( + ThreadAwareCallback<FuncType_3>::Create(TestCallback_3)); + callback_3->RunOnTargetThread(1, &double_arg, &bool_arg); + + typedef void (*FuncType_4)(int, const double*, bool*, TestParameter); + scoped_ptr<ThreadAwareCallback<FuncType_4> > callback_4( + ThreadAwareCallback<FuncType_4>::Create(TestCallback_4)); + callback_4->RunOnTargetThread(1, &double_arg, &bool_arg, object_arg); + + typedef void (*FuncType_5)(int, + const double*, + bool*, + TestParameter, + const TestParameter&); + scoped_ptr<ThreadAwareCallback<FuncType_5> > callback_5( + ThreadAwareCallback<FuncType_5>::Create(TestCallback_5)); + callback_5->RunOnTargetThread(1, &double_arg, &bool_arg, object_arg, + object_arg); + + EXPECT_EQ(6, called_num); +} + +TEST_F(ThreadAwareCallbackMultiThreadTest, RunOnTargetThread) { + RunTest(); +} + +TEST_F(ThreadAwareCallbackAbortTest, NotRunIfAborted) { + RunTest(); +} + +} // namespace ppapi diff --git a/webkit/plugins/ppapi/host_globals.cc b/webkit/plugins/ppapi/host_globals.cc index b337689..32fac79 100644 --- a/webkit/plugins/ppapi/host_globals.cc +++ b/webkit/plugins/ppapi/host_globals.cc @@ -79,8 +79,9 @@ HostGlobals::HostGlobals() : ::ppapi::PpapiGlobals() { host_globals_ = this; } -HostGlobals::HostGlobals(::ppapi::PpapiGlobals::ForTest for_test) - : ::ppapi::PpapiGlobals(for_test) { +HostGlobals::HostGlobals( + ::ppapi::PpapiGlobals::PerThreadForTest per_thread_for_test) + : ::ppapi::PpapiGlobals(per_thread_for_test) { DCHECK(!host_globals_); } diff --git a/webkit/plugins/ppapi/host_globals.h b/webkit/plugins/ppapi/host_globals.h index 0427b2a..31c22ed 100644 --- a/webkit/plugins/ppapi/host_globals.h +++ b/webkit/plugins/ppapi/host_globals.h @@ -22,7 +22,7 @@ class PluginModule; class HostGlobals : public ::ppapi::PpapiGlobals { public: HostGlobals(); - HostGlobals(::ppapi::PpapiGlobals::ForTest); + explicit HostGlobals(::ppapi::PpapiGlobals::PerThreadForTest); virtual ~HostGlobals(); // Getter for the global singleton. Generally, you should use |