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.
8 #include "base/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/ppapi_globals.h"
14 #include "ppapi/shared_impl/ppb_var_shared.h"
17 std::string
VarToString(const PP_Var
& var
, const PPB_Var
* ppb_var
) {
19 const char* utf8
= ppb_var
->VarToUtf8(var
, &len
);
20 return std::string(utf8
, len
);
22 const size_t kNumStrings
= 100;
23 const size_t kNumThreads
= 20;
24 const int kRefsToAdd
= 20;
30 class PPB_VarTest
: public PluginProxyTest
{
33 : test_strings_(kNumStrings
), vars_(kNumStrings
),
34 ppb_var_(ppapi::PPB_Var_Shared::GetVarInterface1_1()) {
35 // Set the value of test_strings_[i] to "i".
36 for (size_t i
= 0; i
< kNumStrings
; ++i
)
37 test_strings_
[i
] = base::IntToString(i
);
40 std::vector
<std::string
> test_strings_
;
41 std::vector
<PP_Var
> vars_
;
42 const PPB_Var
* ppb_var_
;
45 // Test basic String operations.
46 TEST_F(PPB_VarTest
, Strings
) {
47 for (size_t i
= 0; i
< kNumStrings
; ++i
) {
48 vars_
[i
] = ppb_var_
->VarFromUtf8(test_strings_
[i
].c_str(),
49 test_strings_
[i
].length());
50 EXPECT_EQ(test_strings_
[i
], VarToString(vars_
[i
], ppb_var_
));
52 // At this point, they should each have a ref count of 1. Add some more.
53 for (int ref
= 0; ref
< kRefsToAdd
; ++ref
) {
54 for (size_t i
= 0; i
< kNumStrings
; ++i
) {
55 ppb_var_
->AddRef(vars_
[i
]);
56 // Make sure the string is still there with the right value.
57 EXPECT_EQ(test_strings_
[i
], VarToString(vars_
[i
], ppb_var_
));
60 for (int ref
= 0; ref
< kRefsToAdd
; ++ref
) {
61 for (size_t i
= 0; i
< kNumStrings
; ++i
) {
62 ppb_var_
->Release(vars_
[i
]);
63 // Make sure the string is still there with the right value.
64 EXPECT_EQ(test_strings_
[i
], VarToString(vars_
[i
], ppb_var_
));
67 // Now remove the ref counts for each string and make sure they are gone.
68 for (size_t i
= 0; i
< kNumStrings
; ++i
) {
69 ppb_var_
->Release(vars_
[i
]);
71 const char* utf8
= ppb_var_
->VarToUtf8(vars_
[i
], &len
);
72 EXPECT_EQ(NULL
, utf8
);
77 // PPB_VarTest.Threads tests string operations accessed by multiple threads.
79 // These three delegate classes which precede the test are for use with
80 // PlatformThread. The test goes roughly like this:
81 // 1) Spawn kNumThreads 'CreateVar' threads, giving each a roughly equal subset
82 // of test_strings_ to 'create'. Each 'CreateVar' thread also converts its
83 // set of vars back in to strings so that the main test thread can verify
84 // their values were correctly converted.
85 // 2) Spawn kNumThreads 'ChangeRefVar' threads. Each of these threads will
86 // incremement & decrement the reference count of ALL vars kRefsToAdd times.
87 // Finally, each thread adds 1 ref count. This leaves each var with a ref-
88 // count of |kNumThreads + 1|. The main test thread removes a ref, leaving
89 // each var with a ref-count of |kNumThreads|.
90 // 3) Spawn kNumThreads 'RemoveVar' threads. Each of these threads releases each
91 // var once. Once all the threads have finished, there should be no vars
93 class CreateVarThreadDelegate
: public base::PlatformThread::Delegate
{
95 // |strings_in|, |vars|, and |strings_out| are arrays, and size is their size.
96 // For each |strings_in[i]|, we will set |vars[i]| using that value. Then we
97 // read the var back out to |strings_out[i]|.
98 CreateVarThreadDelegate(PP_Module pp_module
, const std::string
* strings_in
,
99 PP_Var
* vars_out
, std::string
* strings_out
,
100 size_t size
, PpapiGlobals
* globals
)
101 : pp_module_(pp_module
), strings_in_(strings_in
), vars_out_(vars_out
),
102 strings_out_(strings_out
), size_(size
), globals_(globals
) {
104 virtual ~CreateVarThreadDelegate() {}
105 virtual void ThreadMain() {
106 PpapiGlobals::SetPpapiGlobalsOnThreadForTest(globals_
);
107 const PPB_Var
* ppb_var
= ppapi::PPB_Var_Shared::GetVarInterface1_1();
108 for (size_t i
= 0; i
< size_
; ++i
) {
109 vars_out_
[i
] = ppb_var
->VarFromUtf8(strings_in_
[i
].c_str(),
110 strings_in_
[i
].length());
111 strings_out_
[i
] = VarToString(vars_out_
[i
], ppb_var
);
115 PP_Module pp_module_
;
116 const std::string
* strings_in_
;
118 std::string
* strings_out_
;
120 PpapiGlobals
* globals_
;
123 // A thread that will increment and decrement the reference count of every var
125 class ChangeRefVarThreadDelegate
: public base::PlatformThread::Delegate
{
127 ChangeRefVarThreadDelegate(const std::vector
<PP_Var
>& vars
,
128 PpapiGlobals
* globals
)
129 : vars_(vars
), globals_(globals
) {
131 virtual ~ChangeRefVarThreadDelegate() {}
132 virtual void ThreadMain() {
133 PpapiGlobals::SetPpapiGlobalsOnThreadForTest(globals_
);
134 const PPB_Var
* ppb_var
= ppapi::PPB_Var_Shared::GetVarInterface1_1();
135 // Increment and decrement the reference count for each var kRefsToAdd
136 // times. Note that we always AddRef once before doing the matching Release,
137 // to ensure that we never accidentally release the last reference.
138 for (int ref
= 0; ref
< kRefsToAdd
; ++ref
) {
139 for (size_t i
= 0; i
< kNumStrings
; ++i
) {
140 ppb_var
->AddRef(vars_
[i
]);
141 ppb_var
->Release(vars_
[i
]);
144 // Now add 1 ref to each Var. The net result is that all Vars will have a
145 // ref-count of (kNumThreads + 1) after this. That will allow us to have all
146 // threads release all vars later.
147 for (size_t i
= 0; i
< kNumStrings
; ++i
) {
148 ppb_var
->AddRef(vars_
[i
]);
152 std::vector
<PP_Var
> vars_
;
153 PpapiGlobals
* globals_
;
156 // A thread that will decrement the reference count of every var once.
157 class RemoveRefVarThreadDelegate
: public base::PlatformThread::Delegate
{
159 RemoveRefVarThreadDelegate(const std::vector
<PP_Var
>& vars
,
160 PpapiGlobals
* globals
)
161 : vars_(vars
), globals_(globals
) {
163 virtual ~RemoveRefVarThreadDelegate() {}
164 virtual void ThreadMain() {
165 PpapiGlobals::SetPpapiGlobalsOnThreadForTest(globals_
);
166 const PPB_Var
* ppb_var
= ppapi::PPB_Var_Shared::GetVarInterface1_1();
167 for (size_t i
= 0; i
< kNumStrings
; ++i
) {
168 ppb_var
->Release(vars_
[i
]);
172 std::vector
<PP_Var
> vars_
;
173 PpapiGlobals
* globals_
;
178 #ifdef ENABLE_PEPPER_THREADING
179 TEST_F(PPB_VarTest
, Threads
) {
181 TEST_F(PPB_VarTest
, DISABLED_Threads
) {
183 std::vector
<base::PlatformThreadHandle
> create_var_threads(kNumThreads
);
184 std::vector
<CreateVarThreadDelegate
> create_var_delegates
;
185 // The strings that the threads will re-extract from Vars (so we can check
186 // that they match the original strings).
187 std::vector
<std::string
> strings_out(kNumStrings
);
188 size_t strings_per_thread
= kNumStrings
/kNumThreads
;
189 // Give each thread an equal slice of strings to turn in to vars. (Except the
190 // last thread may get fewer if kNumStrings is not evenly divisible by
192 for (size_t slice_start
= 0; slice_start
< kNumStrings
;
193 slice_start
+= strings_per_thread
) {
194 create_var_delegates
.push_back(
195 CreateVarThreadDelegate(pp_module(),
196 &test_strings_
[slice_start
],
198 &strings_out
[slice_start
],
199 std::min(strings_per_thread
,
200 kNumStrings
- slice_start
),
203 // Now run then join all the threads.
204 for (size_t i
= 0; i
< kNumThreads
; ++i
)
205 base::PlatformThread::Create(0, &create_var_delegates
[i
],
206 &create_var_threads
[i
]);
207 for (size_t i
= 0; i
< kNumThreads
; ++i
)
208 base::PlatformThread::Join(create_var_threads
[i
]);
209 // Now check that the strings have the expected values.
210 EXPECT_EQ(test_strings_
, strings_out
);
212 // Tinker with the reference counts in a multithreaded way.
213 std::vector
<base::PlatformThreadHandle
> change_ref_var_threads(kNumThreads
);
214 std::vector
<ChangeRefVarThreadDelegate
> change_ref_var_delegates
;
215 for (size_t i
= 0; i
< kNumThreads
; ++i
)
216 change_ref_var_delegates
.push_back(
217 ChangeRefVarThreadDelegate(vars_
, GetGlobals()));
218 for (size_t i
= 0; i
< kNumThreads
; ++i
) {
219 base::PlatformThread::Create(0, &change_ref_var_delegates
[i
],
220 &change_ref_var_threads
[i
]);
222 for (size_t i
= 0; i
< kNumThreads
; ++i
)
223 base::PlatformThread::Join(change_ref_var_threads
[i
]);
225 // Now each var has a refcount of (kNumThreads + 1). Let's decrement each var
226 // once so that every 'RemoveRef' thread (spawned below) owns 1 reference, and
227 // when the last one removes a ref, the Var will be deleted.
228 for (size_t i
= 0; i
< kNumStrings
; ++i
) {
229 ppb_var_
->Release(vars_
[i
]);
232 // Check that all vars are still valid and have the values we expect.
233 for (size_t i
= 0; i
< kNumStrings
; ++i
)
234 EXPECT_EQ(test_strings_
[i
], VarToString(vars_
[i
], ppb_var_
));
236 // Remove the last reference counts for all vars.
237 std::vector
<base::PlatformThreadHandle
> remove_ref_var_threads(kNumThreads
);
238 std::vector
<RemoveRefVarThreadDelegate
> remove_ref_var_delegates
;
239 for (size_t i
= 0; i
< kNumThreads
; ++i
)
240 remove_ref_var_delegates
.push_back(
241 RemoveRefVarThreadDelegate(vars_
, GetGlobals()));
242 for (size_t i
= 0; i
< kNumThreads
; ++i
) {
243 base::PlatformThread::Create(0, &remove_ref_var_delegates
[i
],
244 &remove_ref_var_threads
[i
]);
246 for (size_t i
= 0; i
< kNumThreads
; ++i
)
247 base::PlatformThread::Join(remove_ref_var_threads
[i
]);
249 // All the vars should no longer represent valid strings.
250 for (size_t i
= 0; i
< kNumStrings
; ++i
) {
252 const char* utf8
= ppb_var_
->VarToUtf8(vars_
[i
], &len
);
253 EXPECT_EQ(NULL
, utf8
);