diff options
-rw-r--r-- | build/common.gypi | 9 | ||||
-rw-r--r-- | ppapi/ppapi_shared.gypi | 2 | ||||
-rw-r--r-- | ppapi/proxy/plugin_resource_tracker.cc | 9 | ||||
-rw-r--r-- | ppapi/proxy/plugin_resource_tracker.h | 4 | ||||
-rw-r--r-- | ppapi/proxy/ppb_core_proxy.cc | 3 | ||||
-rw-r--r-- | ppapi/proxy/ppb_var_proxy.cc | 5 | ||||
-rw-r--r-- | ppapi/proxy/ppb_var_unittest.cc | 227 | ||||
-rw-r--r-- | ppapi/shared_impl/proxy_lock.cc | 35 | ||||
-rw-r--r-- | ppapi/shared_impl/proxy_lock.h | 80 | ||||
-rw-r--r-- | ppapi/thunk/enter.cc | 1 | ||||
-rw-r--r-- | ppapi/thunk/enter.h | 58 |
11 files changed, 389 insertions, 44 deletions
diff --git a/build/common.gypi b/build/common.gypi index eb1a36f..435f7e1 100644 --- a/build/common.gypi +++ b/build/common.gypi @@ -221,6 +221,11 @@ # Webrtc compilation is enabled by default. Set to 0 to disable. 'enable_webrtc%': 1, + # PPAPI by default does not support plugins making calls off the main + # thread. Set to 1 to turn on experimental support for out-of-process + # plugins to make call of the main thread. + 'enable_pepper_threading%': 0, + # XInput2 multitouch support is disabled by default (use_xi2_mt=0). # Setting to non-zero value enables XI2 MT. When XI2 MT is enabled, # the input value also defines the required XI2 minor minimum version. @@ -345,6 +350,7 @@ 'use_gnome_keyring%': '<(use_gnome_keyring)', 'linux_fpic%': '<(linux_fpic)', 'enable_flapper_hacks%': '<(enable_flapper_hacks)', + 'enable_pepper_threading%': '<(enable_pepper_threading)', 'chromeos%': '<(chromeos)', 'touchui%': '<(touchui)', 'use_virtual_keyboard%': '<(use_virtual_keyboard)', @@ -975,6 +981,9 @@ ['enable_flapper_hacks==1', { 'defines': ['ENABLE_FLAPPER_HACKS=1'], }], + ['enable_pepper_threading==1', { + 'defines': ['ENABLE_PEPPER_THREADING'], + }], ['configuration_policy==1', { 'defines': ['ENABLE_CONFIGURATION_POLICY'], }], diff --git a/ppapi/ppapi_shared.gypi b/ppapi/ppapi_shared.gypi index cc43fd2..06e3e67 100644 --- a/ppapi/ppapi_shared.gypi +++ b/ppapi/ppapi_shared.gypi @@ -60,6 +60,8 @@ 'shared_impl/ppapi_preferences.h', 'shared_impl/ppp_instance_combined.cc', 'shared_impl/ppp_instance_combined.h', + 'shared_impl/proxy_lock.cc', + 'shared_impl/proxy_lock.h', 'shared_impl/resource.cc', 'shared_impl/resource.h', 'shared_impl/resource_tracker.cc', diff --git a/ppapi/proxy/plugin_resource_tracker.cc b/ppapi/proxy/plugin_resource_tracker.cc index e47c35e..4abd8f7 100644 --- a/ppapi/proxy/plugin_resource_tracker.cc +++ b/ppapi/proxy/plugin_resource_tracker.cc @@ -9,6 +9,7 @@ #include "ppapi/proxy/plugin_dispatcher.h" #include "ppapi/proxy/ppapi_messages.h" #include "ppapi/proxy/serialized_var.h" +#include "ppapi/shared_impl/proxy_lock.h" #include "ppapi/shared_impl/resource.h" #include "ppapi/shared_impl/tracker_base.h" #include "ppapi/shared_impl/var.h" @@ -29,9 +30,17 @@ TrackerBase* GetTrackerBase() { PluginResourceTracker::PluginResourceTracker() : var_tracker_test_override_(NULL) { +#ifdef ENABLE_PEPPER_THREADING + // Set the global proxy lock, since the plugin-side of the proxy needs to be + // synchronized. + ppapi::ProxyLock::Set(&proxy_lock_); +#endif } PluginResourceTracker::~PluginResourceTracker() { +#ifdef ENABLE_PEPPER_THREADING + ppapi::ProxyLock::Reset(); +#endif } // static diff --git a/ppapi/proxy/plugin_resource_tracker.h b/ppapi/proxy/plugin_resource_tracker.h index 7268ddc..30fafb2 100644 --- a/ppapi/proxy/plugin_resource_tracker.h +++ b/ppapi/proxy/plugin_resource_tracker.h @@ -9,6 +9,7 @@ #include <utility> #include "base/compiler_specific.h" +#include "base/synchronization/lock.h" #include "ppapi/c/pp_completion_callback.h" #include "ppapi/c/pp_instance.h" #include "ppapi/c/pp_stdint.h" @@ -91,6 +92,9 @@ class PPAPI_PROXY_EXPORT PluginResourceTracker : public TrackerBase, typedef std::map<HostResource, PP_Resource> HostResourceMap; HostResourceMap host_resource_map_; + // The global lock for the plugin side of the proxy. + base::Lock proxy_lock_; + DISALLOW_COPY_AND_ASSIGN(PluginResourceTracker); }; diff --git a/ppapi/proxy/ppb_core_proxy.cc b/ppapi/proxy/ppb_core_proxy.cc index 7e87241..9173df6 100644 --- a/ppapi/proxy/ppb_core_proxy.cc +++ b/ppapi/proxy/ppb_core_proxy.cc @@ -18,6 +18,7 @@ #include "ppapi/proxy/plugin_dispatcher.h" #include "ppapi/proxy/plugin_resource_tracker.h" #include "ppapi/proxy/ppapi_messages.h" +#include "ppapi/shared_impl/proxy_lock.h" #include "ppapi/shared_impl/time_conversion.h" namespace ppapi { @@ -32,10 +33,12 @@ base::MessageLoopProxy* GetMainThreadMessageLoop() { } void AddRefResource(PP_Resource resource) { + ppapi::ProxyAutoLock lock; PluginResourceTracker::GetInstance()->AddRefResource(resource); } void ReleaseResource(PP_Resource resource) { + ppapi::ProxyAutoLock lock; PluginResourceTracker::GetInstance()->ReleaseResource(resource); } diff --git a/ppapi/proxy/ppb_var_proxy.cc b/ppapi/proxy/ppb_var_proxy.cc index 4ee6cc1..0c7cf5b 100644 --- a/ppapi/proxy/ppb_var_proxy.cc +++ b/ppapi/proxy/ppb_var_proxy.cc @@ -8,6 +8,7 @@ #include "ppapi/c/ppb_var.h" #include "ppapi/proxy/plugin_resource_tracker.h" #include "ppapi/proxy/plugin_var_tracker.h" +#include "ppapi/shared_impl/proxy_lock.h" #include "ppapi/shared_impl/var.h" namespace ppapi { @@ -18,18 +19,22 @@ namespace { // PPP_Var plugin -------------------------------------------------------------- void AddRefVar(PP_Var var) { + ppapi::ProxyAutoLock lock; PluginResourceTracker::GetInstance()->var_tracker().AddRefVar(var); } void ReleaseVar(PP_Var var) { + ppapi::ProxyAutoLock lock; PluginResourceTracker::GetInstance()->var_tracker().ReleaseVar(var); } PP_Var VarFromUtf8(PP_Module module, const char* data, uint32_t len) { + ppapi::ProxyAutoLock lock; return StringVar::StringToPPVar(module, data, len); } const char* VarToUtf8(PP_Var var, uint32_t* len) { + ppapi::ProxyAutoLock lock; StringVar* str = StringVar::FromPPVar(var); if (str) { *len = static_cast<uint32_t>(str->value().size()); diff --git a/ppapi/proxy/ppb_var_unittest.cc b/ppapi/proxy/ppb_var_unittest.cc index 79abf39..16683d9 100644 --- a/ppapi/proxy/ppb_var_unittest.cc +++ b/ppapi/proxy/ppb_var_unittest.cc @@ -6,20 +6,21 @@ #include <vector> #include "base/string_number_conversions.h" +#include "base/threading/platform_thread.h" #include "ppapi/c/pp_var.h" #include "ppapi/c/ppb_var.h" #include "ppapi/proxy/ppapi_proxy_test.h" #include "ppapi/proxy/ppb_var_proxy.h" -// TODO(dmichael): Make PPB_Var_Proxy and PluginResourceTracker thread-safe and -// add thread-safety tests here. - namespace { std::string VarToString(const PP_Var& var, const PPB_Var* ppb_var) { uint32_t len = 0; const char* utf8 = ppb_var->VarToUtf8(var, &len); return std::string(utf8, len); } +const size_t kNumStrings = 100; +const size_t kNumThreads = 20; +const int kRefsToAdd = 20; } // namespace namespace ppapi { @@ -27,46 +28,216 @@ namespace proxy { class PPB_VarTest : public PluginProxyTest { public: - PPB_VarTest() {} + PPB_VarTest() + : test_strings_(kNumStrings), vars_(kNumStrings), + ppb_var_(GetPPB_Var_Interface()) { + // Set the value of test_strings_[i] to "i". + for (size_t i = 0; i < kNumStrings; ++i) + test_strings_[i] = base::IntToString(i); + } + protected: + std::vector<std::string> test_strings_; + std::vector<PP_Var> vars_; + const PPB_Var* ppb_var_; }; +// Test basic String operations. TEST_F(PPB_VarTest, Strings) { - const PPB_Var* ppb_var = GetPPB_Var_Interface(); - - // Make a vector of strings, where the value of test_strings[i] is "i". - const int kNumStrings = 5; - std::vector<std::string> test_strings(kNumStrings); - for (int i = 0; i < kNumStrings; ++i) - test_strings[i] = base::IntToString(i); - - std::vector<PP_Var> vars(kNumStrings); - for (int i = 0; i < kNumStrings; ++i) { - vars[i] = ppb_var->VarFromUtf8(pp_module(), - test_strings[i].c_str(), - test_strings[i].length()); - EXPECT_EQ(test_strings[i], VarToString(vars[i], ppb_var)); + for (size_t i = 0; i < kNumStrings; ++i) { + vars_[i] = ppb_var_->VarFromUtf8(pp_module(), + test_strings_[i].c_str(), + test_strings_[i].length()); + EXPECT_EQ(test_strings_[i], VarToString(vars_[i], ppb_var_)); } // At this point, they should each have a ref count of 1. Add some more. - const int kRefsToAdd = 3; for (int ref = 0; ref < kRefsToAdd; ++ref) { - for (int i = 0; i < kNumStrings; ++i) { - ppb_var->AddRef(vars[i]); + for (size_t i = 0; i < kNumStrings; ++i) { + ppb_var_->AddRef(vars_[i]); // Make sure the string is still there with the right value. - EXPECT_EQ(test_strings[i], VarToString(vars[i], ppb_var)); + EXPECT_EQ(test_strings_[i], VarToString(vars_[i], ppb_var_)); } } for (int ref = 0; ref < kRefsToAdd; ++ref) { - for (int i = 0; i < kNumStrings; ++i) { - ppb_var->Release(vars[i]); + for (size_t i = 0; i < kNumStrings; ++i) { + ppb_var_->Release(vars_[i]); // Make sure the string is still there with the right value. - EXPECT_EQ(test_strings[i], VarToString(vars[i], ppb_var)); + EXPECT_EQ(test_strings_[i], VarToString(vars_[i], ppb_var_)); } } // Now remove the ref counts for each string and make sure they are gone. - for (int i = 0; i < kNumStrings; ++i) { - ppb_var->Release(vars[i]); + for (size_t i = 0; i < kNumStrings; ++i) { + ppb_var_->Release(vars_[i]); + uint32_t len = 10; + const char* utf8 = ppb_var_->VarToUtf8(vars_[i], &len); + EXPECT_EQ(NULL, utf8); + EXPECT_EQ(0u, len); + } +} + +// PPB_VarTest.Threads tests string operations accessed by multiple threads. +namespace { +// These three delegate classes which precede the test are for use with +// PlatformThread. The test goes roughly like this: +// 1) Spawn kNumThreads 'CreateVar' threads, giving each a roughly equal subset +// of test_strings_ to 'create'. Each 'CreateVar' thread also converts its +// set of vars back in to strings so that the main test thread can verify +// their values were correctly converted. +// 2) Spawn kNumThreads 'ChangeRefVar' threads. Each of these threads will +// incremement & decrement the reference count of ALL vars kRefsToAdd times. +// Finally, each thread adds 1 ref count. This leaves each var with a ref- +// count of |kNumThreads + 1|. The main test thread removes a ref, leaving +// each var with a ref-count of |kNumThreads|. +// 3) Spawn kNumThreads 'RemoveVar' threads. Each of these threads releases each +// var once. Once all the threads have finished, there should be no vars +// left. +class CreateVarThreadDelegate : public base::PlatformThread::Delegate { + public: + // |strings_in|, |vars|, and |strings_out| are arrays, and size is their size. + // For each |strings_in[i]|, we will set |vars[i]| using that value. Then we + // 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) + : pp_module_(pp_module), strings_in_(strings_in), vars_out_(vars_out), + strings_out_(strings_out), size_(size) { + } + virtual ~CreateVarThreadDelegate() {} + virtual void ThreadMain() { + const PPB_Var* ppb_var = ppapi::proxy::GetPPB_Var_Interface(); + for (size_t i = 0; i < size_; ++i) { + vars_out_[i] = ppb_var->VarFromUtf8(pp_module_, + strings_in_[i].c_str(), + strings_in_[i].length()); + strings_out_[i] = VarToString(vars_out_[i], ppb_var); + } + } + private: + PP_Module pp_module_; + const std::string* strings_in_; + PP_Var* vars_out_; + std::string* strings_out_; + size_t size_; +}; + +// 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) : vars_(vars) { + } + virtual ~ChangeRefVarThreadDelegate() {} + virtual void ThreadMain() { + const PPB_Var* ppb_var = ppapi::proxy::GetPPB_Var_Interface(); + // Increment and decrement the reference count for each var kRefsToAdd + // times. Note that we always AddRef once before doing the matching Release, + // to ensure that we never accidentally release the last reference. + for (int ref = 0; ref < kRefsToAdd; ++ref) { + for (size_t i = 0; i < kNumStrings; ++i) { + ppb_var->AddRef(vars_[i]); + ppb_var->Release(vars_[i]); + } + } + // Now add 1 ref to each Var. The net result is that all Vars will have a + // ref-count of (kNumThreads + 1) after this. That will allow us to have all + // threads release all vars later. + for (size_t i = 0; i < kNumStrings; ++i) { + ppb_var->AddRef(vars_[i]); + } + } + private: + std::vector<PP_Var> vars_; +}; + +// 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) : vars_(vars) { + } + virtual ~RemoveRefVarThreadDelegate() {} + virtual void ThreadMain() { + const PPB_Var* ppb_var = ppapi::proxy::GetPPB_Var_Interface(); + for (size_t i = 0; i < kNumStrings; ++i) { + ppb_var->Release(vars_[i]); + } + } + private: + std::vector<PP_Var> vars_; +}; + +} // namespace + +#ifdef ENABLE_PEPPER_THREADING +TEST_F(PPB_VarTest, Threads) { +#else +TEST_F(PPB_VarTest, DISABLED_Threads) { +#endif + std::vector<base::PlatformThreadHandle> create_var_threads(kNumThreads); + std::vector<CreateVarThreadDelegate> create_var_delegates; + // The strings that the threads will re-extract from Vars (so we can check + // that they match the original strings). + std::vector<std::string> strings_out(kNumStrings); + size_t strings_per_thread = kNumStrings/kNumThreads; + // Give each thread an equal slice of strings to turn in to vars. (Except the + // last thread may get fewer if kNumStrings is not evenly divisible by + // kNumThreads). + for (size_t slice_start= 0; slice_start < kNumStrings; + slice_start += strings_per_thread) { + create_var_delegates.push_back( + CreateVarThreadDelegate(pp_module(), + &test_strings_[slice_start], + &vars_[slice_start], + &strings_out[slice_start], + std::min(strings_per_thread, + kNumStrings - slice_start))); + } + // Now run then join all the threads. + for (size_t i = 0; i < kNumThreads; ++i) + base::PlatformThread::Create(0, &create_var_delegates[i], + &create_var_threads[i]); + for (size_t i = 0; i < kNumThreads; ++i) + base::PlatformThread::Join(create_var_threads[i]); + // Now check that the strings have the expected values. + EXPECT_EQ(test_strings_, strings_out); + + // Tinker with the reference counts in a multithreaded way. + 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_)); + for (size_t i = 0; i < kNumThreads; ++i) { + base::PlatformThread::Create(0, &change_ref_var_delegates[i], + &change_ref_var_threads[i]); + } + for (size_t i = 0; i < kNumThreads; ++i) + base::PlatformThread::Join(change_ref_var_threads[i]); + + // Now each var has a refcount of (kNumThreads + 1). Let's decrement each var + // once so that every 'RemoveRef' thread (spawned below) owns 1 reference, and + // when the last one removes a ref, the Var will be deleted. + for (size_t i = 0; i < kNumStrings; ++i) { + ppb_var_->Release(vars_[i]); + } + + // Check that all vars are still valid and have the values we expect. + for (size_t i = 0; i < kNumStrings; ++i) + EXPECT_EQ(test_strings_[i], VarToString(vars_[i], ppb_var_)); + + // Remove the last reference counts for all vars. + 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_)); + for (size_t i = 0; i < kNumThreads; ++i) { + base::PlatformThread::Create(0, &remove_ref_var_delegates[i], + &remove_ref_var_threads[i]); + } + for (size_t i = 0; i < kNumThreads; ++i) + base::PlatformThread::Join(remove_ref_var_threads[i]); + + // All the vars should no longer represent valid strings. + for (size_t i = 0; i < kNumStrings; ++i) { uint32_t len = 10; - const char* utf8 = ppb_var->VarToUtf8(vars[i], &len); + const char* utf8 = ppb_var_->VarToUtf8(vars_[i], &len); EXPECT_EQ(NULL, utf8); EXPECT_EQ(0u, len); } diff --git a/ppapi/shared_impl/proxy_lock.cc b/ppapi/shared_impl/proxy_lock.cc new file mode 100644 index 0000000..3d84930 --- /dev/null +++ b/ppapi/shared_impl/proxy_lock.cc @@ -0,0 +1,35 @@ +// Copyright (c) 2011 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/proxy_lock.h" + +#include "base/synchronization/lock.h" + +namespace ppapi { + +base::Lock* ProxyLock::lock_ = NULL; + +// static +void ProxyLock::Acquire() { + if (lock_) + lock_->Acquire(); +} + +// static +void ProxyLock::Release() { + if (lock_) + lock_->Release(); +} + +// static +void ProxyLock::Set(base::Lock* lock) { + lock_ = lock; +} + +// static +void ProxyLock::Reset() { + Set(NULL); +} + +} // namespace ppapi diff --git a/ppapi/shared_impl/proxy_lock.h b/ppapi/shared_impl/proxy_lock.h new file mode 100644 index 0000000..aa6120e --- /dev/null +++ b/ppapi/shared_impl/proxy_lock.h @@ -0,0 +1,80 @@ +// Copyright (c) 2011 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_PROXY_LOCK_H_ +#define PPAPI_SHARED_IMPL_PROXY_LOCK_H_ + +#include "base/basictypes.h" + +namespace base { +class Lock; +} + +namespace ppapi { + +// This is the one lock to rule them all for the ppapi proxy. All PPB interface +// functions that need to be synchronized should lock this lock on entry. This +// is normally accomplished by using an appropriate Enter RAII object at the +// beginning of each thunk function. +// +// TODO(dmichael): If this turns out to be too slow and contentious, we'll want +// to use multiple locks. E.g., one for the var tracker, one for the resource +// tracker, etc. +class ProxyLock { + public: + // Acquire the proxy lock. If it is currently held by another thread, block + // until it is available. If the lock has not been set using the 'Set' method, + // this operation does nothing. That is the normal case for the host side; + // see PluginResourceTracker for where the lock gets set for the out-of- + // process plugin case. + static void Acquire(); + // Relinquish the proxy lock. If the lock has not been set, this does nothing. + static void Release(); + + // Set the lock that ProxyLock will use. The caller is responsible for + // ensuring that the lock stays valid so long as the ProxyLock may be in use. + static void Set(base::Lock* lock); + // Set the lock to NULL. + static void Reset(); + private: + static base::Lock* lock_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(ProxyLock); +}; + +// A simple RAII class for locking the PPAPI proxy lock on entry and releasing +// on exit. This is for simple interfaces that don't use the 'thunk' system, +// such as PPB_Var and PPB_Core. +class ProxyAutoLock { + public: + ProxyAutoLock() { + ProxyLock::Acquire(); + } + ~ProxyAutoLock() { + ProxyLock::Release(); + } + private: + DISALLOW_COPY_AND_ASSIGN(ProxyAutoLock); +}; + +// The inverse of the above; unlock on construction, lock on destruction. This +// is useful for calling out to the plugin, when we need to unlock but ensure +// that we re-acquire the lock when the plugin is returns or raises an +// exception. +class ProxyAutoUnlock { + public: + ProxyAutoUnlock() { + ProxyLock::Release(); + } + ~ProxyAutoUnlock() { + ProxyLock::Acquire(); + } + private: + DISALLOW_COPY_AND_ASSIGN(ProxyAutoUnlock); +}; + + +} // namespace ppapi + +#endif // PPAPI_SHARED_IMPL_PROXY_LOCK_H_ diff --git a/ppapi/thunk/enter.cc b/ppapi/thunk/enter.cc index da3f85c..7e9f70a 100644 --- a/ppapi/thunk/enter.cc +++ b/ppapi/thunk/enter.cc @@ -4,6 +4,7 @@ #include "ppapi/thunk/enter.h" +#include "base/lazy_instance.h" #include "ppapi/thunk/ppb_instance_api.h" #include "ppapi/thunk/resource_creation_api.h" diff --git a/ppapi/thunk/enter.h b/ppapi/thunk/enter.h index dd6a632..34fb706 100644 --- a/ppapi/thunk/enter.h +++ b/ppapi/thunk/enter.h @@ -9,6 +9,7 @@ #include "ppapi/c/pp_resource.h" #include "ppapi/proxy/interface_id.h" #include "ppapi/shared_impl/function_group_base.h" +#include "ppapi/shared_impl/proxy_lock.h" #include "ppapi/shared_impl/resource.h" #include "ppapi/shared_impl/tracker_base.h" #include "ppapi/shared_impl/resource_tracker.h" @@ -19,7 +20,7 @@ namespace ppapi { namespace thunk { -// EnterHost* helper objects: These objects wrap a call from the C PPAPI into +// Enter* helper objects: These objects wrap a call from the C PPAPI into // the internal implementation. They make sure the lock is acquired and will // automatically set up some stuff for you. // @@ -29,19 +30,47 @@ namespace thunk { // The |report_error| arguments to the constructor should indicate if errors // should be logged to the console. If the calling function expects that the // input values are correct (the normal case), this should be set to true. In -// some case like |IsFoo(PP_Resource)| the caller is quersioning whether their +// some case like |IsFoo(PP_Resource)| the caller is questioning whether their // handle is this type, and we don't want to report an error if it's not. // -// Standalone functions: EnterHostFunction +// Standalone functions: EnterFunction // Automatically gets the implementation for the function API for the // supplied PP_Instance. // -// Resource member functions: EnterHostResource +// Resource member functions: EnterResource // Automatically interprets the given PP_Resource as a resource ID and sets // up the resource object for you. -template<typename FunctionsT> -class EnterFunction { +namespace subtle { + +// This helps us define our RAII Enter classes easily. To make an RAII class +// which locks the proxy lock on construction and unlocks on destruction, +// inherit from |LockOnEntry<true>|. For cases where you don't want to lock, +// inherit from |LockOnEntry<false>|. This allows us to share more code between +// Enter* and Enter*NoLock classes. +template <bool lock_on_entry> +struct LockOnEntry; + +template <> +struct LockOnEntry<false> { +// TODO(dmichael) assert the lock is held. +}; + +template <> +struct LockOnEntry<true> { + LockOnEntry() { + ppapi::ProxyLock::Acquire(); + } + ~LockOnEntry() { + ppapi::ProxyLock::Release(); + } +}; + +} // namespace subtle + + +template<typename FunctionsT, bool lock_on_entry = true> +class EnterFunction : subtle::LockOnEntry<lock_on_entry> { public: EnterFunction(PP_Instance instance, bool report_error) : functions_(NULL) { @@ -51,6 +80,7 @@ class EnterFunction { functions_ = base->GetAs<FunctionsT>(); // TODO(brettw) check error and if report_error is set, do something. } + ~EnterFunction() {} bool succeeded() const { return !!functions_; } @@ -65,13 +95,11 @@ class EnterFunction { }; // Like EnterResource but assumes the lock is already held. -// TODO(brettw) actually implement locks, this is just a placeholder for now. template<typename FunctionsT> -class EnterFunctionNoLock : public EnterFunction<FunctionsT> { +class EnterFunctionNoLock : public EnterFunction<FunctionsT, false> { public: EnterFunctionNoLock(PP_Instance instance, bool report_error) - : EnterFunction<FunctionsT>(instance, report_error) { - // TODO(brettw) assert the lock is held. + : EnterFunction<FunctionsT, false>(instance, report_error) { } }; @@ -95,8 +123,8 @@ class EnterFunctionGivenResource : public EnterFunction<FunctionsT> { // EnterResource --------------------------------------------------------------- -template<typename ResourceT> -class EnterResource { +template<typename ResourceT, bool lock_on_entry = true> +class EnterResource : subtle::LockOnEntry<lock_on_entry> { public: EnterResource(PP_Resource resource, bool report_error) : object_(NULL) { @@ -121,13 +149,11 @@ class EnterResource { }; // Like EnterResource but assumes the lock is already held. -// TODO(brettw) actually implement locks, this is just a placeholder for now. template<typename ResourceT> -class EnterResourceNoLock : public EnterResource<ResourceT> { +class EnterResourceNoLock : public EnterResource<ResourceT, false> { public: EnterResourceNoLock(PP_Resource resource, bool report_error) - : EnterResource<ResourceT>(resource, report_error) { - // TODO(brettw) assert the lock is held. + : EnterResource<ResourceT, false>(resource, report_error) { } }; |