diff options
author | dmichael@chromium.org <dmichael@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-10-11 18:58:22 +0000 |
---|---|---|
committer | dmichael@chromium.org <dmichael@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-10-11 18:58:22 +0000 |
commit | 67c125db892ec5222d74012da3c6d353cdf24671 (patch) | |
tree | f8380f814530c5b6a05543c91cc3c68212454ad3 /ppapi/proxy/ppb_var_unittest.cc | |
parent | 339651d588733a950864e7cac566b614421ca9cd (diff) | |
download | chromium_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.cc | 227 |
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); } |