Home | History | Annotate | Download | only in proxy
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include <string>
      6 #include <vector>
      7 
      8 #include "base/strings/string_number_conversions.h"
      9 #include "base/threading/platform_thread.h"
     10 #include "ppapi/c/pp_var.h"
     11 #include "ppapi/c/ppb_var.h"
     12 #include "ppapi/proxy/ppapi_proxy_test.h"
     13 #include "ppapi/shared_impl/ppb_var_shared.h"
     14 
     15 namespace {
     16 std::string VarToString(const PP_Var& var, const PPB_Var* ppb_var) {
     17   uint32_t len = 0;
     18   const char* utf8 = ppb_var->VarToUtf8(var, &len);
     19   return std::string(utf8, len);
     20 }
     21 const size_t kNumStrings = 100;
     22 const size_t kNumThreads = 20;
     23 const int kRefsToAdd = 20;
     24 }  // namespace
     25 
     26 namespace ppapi {
     27 namespace proxy {
     28 
     29 class PPB_VarTest : public PluginProxyTest {
     30  public:
     31   PPB_VarTest()
     32       : test_strings_(kNumStrings), vars_(kNumStrings),
     33         ppb_var_(ppapi::PPB_Var_Shared::GetVarInterface1_2()) {
     34     // Set the value of test_strings_[i] to "i".
     35     for (size_t i = 0; i < kNumStrings; ++i)
     36       test_strings_[i] = base::IntToString(i);
     37   }
     38  protected:
     39   std::vector<std::string> test_strings_;
     40   std::vector<PP_Var> vars_;
     41   const PPB_Var* ppb_var_;
     42 };
     43 
     44 // Test basic String operations.
     45 TEST_F(PPB_VarTest, Strings) {
     46   for (size_t i = 0; i < kNumStrings; ++i) {
     47     vars_[i] = ppb_var_->VarFromUtf8(test_strings_[i].c_str(),
     48                                      test_strings_[i].length());
     49     EXPECT_EQ(test_strings_[i], VarToString(vars_[i], ppb_var_));
     50   }
     51   // At this point, they should each have a ref count of 1. Add some more.
     52   for (int ref = 0; ref < kRefsToAdd; ++ref) {
     53     for (size_t i = 0; i < kNumStrings; ++i) {
     54       ppb_var_->AddRef(vars_[i]);
     55       // Make sure the string is still there with the right value.
     56       EXPECT_EQ(test_strings_[i], VarToString(vars_[i], ppb_var_));
     57     }
     58   }
     59   for (int ref = 0; ref < kRefsToAdd; ++ref) {
     60     for (size_t i = 0; i < kNumStrings; ++i) {
     61       ppb_var_->Release(vars_[i]);
     62       // Make sure the string is still there with the right value.
     63       EXPECT_EQ(test_strings_[i], VarToString(vars_[i], ppb_var_));
     64     }
     65   }
     66   // Now remove the ref counts for each string and make sure they are gone.
     67   for (size_t i = 0; i < kNumStrings; ++i) {
     68     ppb_var_->Release(vars_[i]);
     69     uint32_t len = 10;
     70     const char* utf8 = ppb_var_->VarToUtf8(vars_[i], &len);
     71     EXPECT_EQ(NULL, utf8);
     72     EXPECT_EQ(0u, len);
     73   }
     74 }
     75 
     76 // PPB_VarTest.Threads tests string operations accessed by multiple threads.
     77 namespace {
     78 // These three delegate classes which precede the test are for use with
     79 // PlatformThread. The test goes roughly like this:
     80 // 1) Spawn kNumThreads 'CreateVar' threads, giving each a roughly equal subset
     81 //    of test_strings_ to 'create'. Each 'CreateVar' thread also converts its
     82 //    set of vars back in to strings so that the main test thread can verify
     83 //    their values were correctly converted.
     84 // 2) Spawn kNumThreads 'ChangeRefVar' threads. Each of these threads will
     85 //    incremement & decrement the reference count of ALL vars kRefsToAdd times.
     86 //    Finally, each thread adds 1 ref count. This leaves each var with a ref-
     87 //    count of |kNumThreads + 1|. The main test thread removes a ref, leaving
     88 //    each var with a ref-count of |kNumThreads|.
     89 // 3) Spawn kNumThreads 'RemoveVar' threads. Each of these threads releases each
     90 //    var once. Once all the threads have finished, there should be no vars
     91 //    left.
     92 class CreateVarThreadDelegate : public base::PlatformThread::Delegate {
     93  public:
     94   // |strings_in|, |vars|, and |strings_out| are arrays, and size is their size.
     95   // For each |strings_in[i]|, we will set |vars[i]| using that value. Then we
     96   // read the var back out to |strings_out[i]|.
     97   CreateVarThreadDelegate(PP_Module pp_module, const std::string* strings_in,
     98                           PP_Var* vars_out, std::string* strings_out,
     99                           size_t size)
    100       : pp_module_(pp_module), strings_in_(strings_in), vars_out_(vars_out),
    101         strings_out_(strings_out), size_(size) {
    102   }
    103   virtual ~CreateVarThreadDelegate() {}
    104   virtual void ThreadMain() {
    105     const PPB_Var* ppb_var = ppapi::PPB_Var_Shared::GetVarInterface1_2();
    106     for (size_t i = 0; i < size_; ++i) {
    107       vars_out_[i] = ppb_var->VarFromUtf8(strings_in_[i].c_str(),
    108                                           strings_in_[i].length());
    109       strings_out_[i] = VarToString(vars_out_[i], ppb_var);
    110     }
    111   }
    112  private:
    113   PP_Module pp_module_;
    114   const std::string* strings_in_;
    115   PP_Var* vars_out_;
    116   std::string* strings_out_;
    117   size_t size_;
    118 };
    119 
    120 // A thread that will increment and decrement the reference count of every var
    121 // multiple times.
    122 class ChangeRefVarThreadDelegate : public base::PlatformThread::Delegate {
    123  public:
    124   ChangeRefVarThreadDelegate(const std::vector<PP_Var>& vars) : vars_(vars) {
    125   }
    126   virtual ~ChangeRefVarThreadDelegate() {}
    127   virtual void ThreadMain() {
    128     const PPB_Var* ppb_var = ppapi::PPB_Var_Shared::GetVarInterface1_2();
    129     // Increment and decrement the reference count for each var kRefsToAdd
    130     // times. Note that we always AddRef once before doing the matching Release,
    131     // to ensure that we never accidentally release the last reference.
    132     for (int ref = 0; ref < kRefsToAdd; ++ref) {
    133       for (size_t i = 0; i < kNumStrings; ++i) {
    134         ppb_var->AddRef(vars_[i]);
    135         ppb_var->Release(vars_[i]);
    136       }
    137     }
    138     // Now add 1 ref to each Var. The net result is that all Vars will have a
    139     // ref-count of (kNumThreads + 1) after this. That will allow us to have all
    140     // threads release all vars later.
    141     for (size_t i = 0; i < kNumStrings; ++i) {
    142       ppb_var->AddRef(vars_[i]);
    143     }
    144   }
    145  private:
    146   std::vector<PP_Var> vars_;
    147 };
    148 
    149 // A thread that will decrement the reference count of every var once.
    150 class RemoveRefVarThreadDelegate : public base::PlatformThread::Delegate {
    151  public:
    152   RemoveRefVarThreadDelegate(const std::vector<PP_Var>& vars) : vars_(vars) {
    153   }
    154   virtual ~RemoveRefVarThreadDelegate() {}
    155   virtual void ThreadMain() {
    156     const PPB_Var* ppb_var = ppapi::PPB_Var_Shared::GetVarInterface1_2();
    157     for (size_t i = 0; i < kNumStrings; ++i) {
    158       ppb_var->Release(vars_[i]);
    159     }
    160   }
    161  private:
    162   std::vector<PP_Var> vars_;
    163 };
    164 
    165 }  // namespace
    166 
    167 TEST_F(PPB_VarTest, Threads) {
    168   std::vector<base::PlatformThreadHandle> create_var_threads(kNumThreads);
    169   std::vector<CreateVarThreadDelegate> create_var_delegates;
    170   // The strings that the threads will re-extract from Vars (so we can check
    171   // that they match the original strings).
    172   std::vector<std::string> strings_out(kNumStrings);
    173   size_t strings_per_thread = kNumStrings/kNumThreads;
    174   // Give each thread an equal slice of strings to turn in to vars. (Except the
    175   // last thread may get fewer if kNumStrings is not evenly divisible by
    176   // kNumThreads).
    177   for (size_t slice_start= 0; slice_start < kNumStrings;
    178        slice_start += strings_per_thread) {
    179     create_var_delegates.push_back(
    180         CreateVarThreadDelegate(pp_module(),
    181                                 &test_strings_[slice_start],
    182                                 &vars_[slice_start],
    183                                 &strings_out[slice_start],
    184                                 std::min(strings_per_thread,
    185                                          kNumStrings - slice_start)));
    186   }
    187   // Now run then join all the threads.
    188   for (size_t i = 0; i < kNumThreads; ++i)
    189     base::PlatformThread::Create(0, &create_var_delegates[i],
    190                                  &create_var_threads[i]);
    191   for (size_t i = 0; i < kNumThreads; ++i)
    192     base::PlatformThread::Join(create_var_threads[i]);
    193   // Now check that the strings have the expected values.
    194   EXPECT_EQ(test_strings_, strings_out);
    195 
    196   // Tinker with the reference counts in a multithreaded way.
    197   std::vector<base::PlatformThreadHandle> change_ref_var_threads(kNumThreads);
    198   std::vector<ChangeRefVarThreadDelegate> change_ref_var_delegates;
    199   for (size_t i = 0; i < kNumThreads; ++i)
    200     change_ref_var_delegates.push_back(ChangeRefVarThreadDelegate(vars_));
    201   for (size_t i = 0; i < kNumThreads; ++i) {
    202     base::PlatformThread::Create(0, &change_ref_var_delegates[i],
    203                                  &change_ref_var_threads[i]);
    204   }
    205   for (size_t i = 0; i < kNumThreads; ++i)
    206     base::PlatformThread::Join(change_ref_var_threads[i]);
    207 
    208   // Now each var has a refcount of (kNumThreads + 1). Let's decrement each var
    209   // once so that every 'RemoveRef' thread (spawned below) owns 1 reference, and
    210   // when the last one removes a ref, the Var will be deleted.
    211   for (size_t i = 0; i < kNumStrings; ++i) {
    212     ppb_var_->Release(vars_[i]);
    213   }
    214 
    215   // Check that all vars are still valid and have the values we expect.
    216   for (size_t i = 0; i < kNumStrings; ++i)
    217     EXPECT_EQ(test_strings_[i], VarToString(vars_[i], ppb_var_));
    218 
    219   // Remove the last reference counts for all vars.
    220   std::vector<base::PlatformThreadHandle> remove_ref_var_threads(kNumThreads);
    221   std::vector<RemoveRefVarThreadDelegate> remove_ref_var_delegates;
    222   for (size_t i = 0; i < kNumThreads; ++i)
    223     remove_ref_var_delegates.push_back(RemoveRefVarThreadDelegate(vars_));
    224   for (size_t i = 0; i < kNumThreads; ++i) {
    225     base::PlatformThread::Create(0, &remove_ref_var_delegates[i],
    226                                  &remove_ref_var_threads[i]);
    227   }
    228   for (size_t i = 0; i < kNumThreads; ++i)
    229     base::PlatformThread::Join(remove_ref_var_threads[i]);
    230 
    231   // All the vars should no longer represent valid strings.
    232   for (size_t i = 0; i < kNumStrings; ++i) {
    233     uint32_t len = 10;
    234     const char* utf8 = ppb_var_->VarToUtf8(vars_[i], &len);
    235     EXPECT_EQ(NULL, utf8);
    236     EXPECT_EQ(0u, len);
    237   }
    238 }
    239 
    240 }  // namespace proxy
    241 }  // namespace ppapi
    242