summaryrefslogtreecommitdiffstats
path: root/ppapi/proxy/ppb_var_unittest.cc
diff options
context:
space:
mode:
authordmichael@chromium.org <dmichael@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-10-11 18:58:22 +0000
committerdmichael@chromium.org <dmichael@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-10-11 18:58:22 +0000
commit67c125db892ec5222d74012da3c6d353cdf24671 (patch)
treef8380f814530c5b6a05543c91cc3c68212454ad3 /ppapi/proxy/ppb_var_unittest.cc
parent339651d588733a950864e7cac566b614421ca9cd (diff)
downloadchromium_src-67c125db892ec5222d74012da3c6d353cdf24671.zip
chromium_src-67c125db892ec5222d74012da3c6d353cdf24671.tar.gz
chromium_src-67c125db892ec5222d74012da3c6d353cdf24671.tar.bz2
Add global lock for Enter* classes. Add ScopedProxyLock for non-thunk proxies. Add multi-threading test for PPB_Var.
BUG=92909 TEST=None yet Review URL: http://codereview.chromium.org/8016008 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@104931 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ppapi/proxy/ppb_var_unittest.cc')
-rw-r--r--ppapi/proxy/ppb_var_unittest.cc227
1 files changed, 199 insertions, 28 deletions
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);
}