summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--build/common.gypi9
-rw-r--r--ppapi/ppapi_shared.gypi2
-rw-r--r--ppapi/proxy/plugin_resource_tracker.cc9
-rw-r--r--ppapi/proxy/plugin_resource_tracker.h4
-rw-r--r--ppapi/proxy/ppb_core_proxy.cc3
-rw-r--r--ppapi/proxy/ppb_var_proxy.cc5
-rw-r--r--ppapi/proxy/ppb_var_unittest.cc227
-rw-r--r--ppapi/shared_impl/proxy_lock.cc35
-rw-r--r--ppapi/shared_impl/proxy_lock.h80
-rw-r--r--ppapi/thunk/enter.cc1
-rw-r--r--ppapi/thunk/enter.h58
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) {
}
};