| Index: ppapi/proxy/ppb_var_unittest.cc
|
| diff --git a/ppapi/proxy/ppb_var_unittest.cc b/ppapi/proxy/ppb_var_unittest.cc
|
| index 79abf39b79b1eae4ba3012862128d1d59a5d0149..16683d939d2fe9c537801ec46542c96085c6473f 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);
|
| }
|
|
|