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