Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / ppapi / proxy / ppb_var_unittest.cc
blob5e25379eeb0213f5c9ef6793a36885fb262ba44a
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.
5 #include <string>
6 #include <vector>
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"
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);
21 const size_t kNumStrings = 100;
22 const size_t kNumThreads = 20;
23 const int kRefsToAdd = 20;
24 } // namespace
26 namespace ppapi {
27 namespace proxy {
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);
38 protected:
39 std::vector<std::string> test_strings_;
40 std::vector<PP_Var> vars_;
41 const PPB_Var* ppb_var_;
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_));
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_));
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_));
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);
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) {
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);
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_;
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) {
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]);
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]);
145 private:
146 std::vector<PP_Var> vars_;
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) {
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]);
161 private:
162 std::vector<PP_Var> vars_;
165 } // namespace
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)));
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);
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]);
205 for (size_t i = 0; i < kNumThreads; ++i)
206 base::PlatformThread::Join(change_ref_var_threads[i]);
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]);
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_));
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]);
228 for (size_t i = 0; i < kNumThreads; ++i)
229 base::PlatformThread::Join(remove_ref_var_threads[i]);
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);
240 } // namespace proxy
241 } // namespace ppapi