| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 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 "base/threading/platform_thread.h" |
| 10 #include "ppapi/c/pp_var.h" | 10 #include "ppapi/c/pp_var.h" |
| 11 #include "ppapi/c/ppb_var.h" | 11 #include "ppapi/c/ppb_var.h" |
| 12 #include "ppapi/proxy/ppapi_proxy_test.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" | 13 #include "ppapi/shared_impl/ppb_var_shared.h" |
| 15 | 14 |
| 16 namespace { | 15 namespace { |
| 17 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) { |
| 18 uint32_t len = 0; | 17 uint32_t len = 0; |
| 19 const char* utf8 = ppb_var->VarToUtf8(var, &len); | 18 const char* utf8 = ppb_var->VarToUtf8(var, &len); |
| 20 return std::string(utf8, len); | 19 return std::string(utf8, len); |
| 21 } | 20 } |
| 22 const size_t kNumStrings = 100; | 21 const size_t kNumStrings = 100; |
| 23 const size_t kNumThreads = 20; | 22 const size_t kNumThreads = 20; |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 90 // 3) Spawn kNumThreads 'RemoveVar' threads. Each of these threads releases each | 89 // 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 | 90 // var once. Once all the threads have finished, there should be no vars |
| 92 // left. | 91 // left. |
| 93 class CreateVarThreadDelegate : public base::PlatformThread::Delegate { | 92 class CreateVarThreadDelegate : public base::PlatformThread::Delegate { |
| 94 public: | 93 public: |
| 95 // |strings_in|, |vars|, and |strings_out| are arrays, and size is their size. | 94 // |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 | 95 // 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]|. | 96 // read the var back out to |strings_out[i]|. |
| 98 CreateVarThreadDelegate(PP_Module pp_module, const std::string* strings_in, | 97 CreateVarThreadDelegate(PP_Module pp_module, const std::string* strings_in, |
| 99 PP_Var* vars_out, std::string* strings_out, | 98 PP_Var* vars_out, std::string* strings_out, |
| 100 size_t size, PpapiGlobals* globals) | 99 size_t size) |
| 101 : pp_module_(pp_module), strings_in_(strings_in), vars_out_(vars_out), | 100 : pp_module_(pp_module), strings_in_(strings_in), vars_out_(vars_out), |
| 102 strings_out_(strings_out), size_(size), globals_(globals) { | 101 strings_out_(strings_out), size_(size) { |
| 103 } | 102 } |
| 104 virtual ~CreateVarThreadDelegate() {} | 103 virtual ~CreateVarThreadDelegate() {} |
| 105 virtual void ThreadMain() { | 104 virtual void ThreadMain() { |
| 106 PpapiGlobals::SetPpapiGlobalsOnThreadForTest(globals_); | |
| 107 const PPB_Var* ppb_var = ppapi::PPB_Var_Shared::GetVarInterface1_1(); | 105 const PPB_Var* ppb_var = ppapi::PPB_Var_Shared::GetVarInterface1_1(); |
| 108 for (size_t i = 0; i < size_; ++i) { | 106 for (size_t i = 0; i < size_; ++i) { |
| 109 vars_out_[i] = ppb_var->VarFromUtf8(strings_in_[i].c_str(), | 107 vars_out_[i] = ppb_var->VarFromUtf8(strings_in_[i].c_str(), |
| 110 strings_in_[i].length()); | 108 strings_in_[i].length()); |
| 111 strings_out_[i] = VarToString(vars_out_[i], ppb_var); | 109 strings_out_[i] = VarToString(vars_out_[i], ppb_var); |
| 112 } | 110 } |
| 113 } | 111 } |
| 114 private: | 112 private: |
| 115 PP_Module pp_module_; | 113 PP_Module pp_module_; |
| 116 const std::string* strings_in_; | 114 const std::string* strings_in_; |
| 117 PP_Var* vars_out_; | 115 PP_Var* vars_out_; |
| 118 std::string* strings_out_; | 116 std::string* strings_out_; |
| 119 size_t size_; | 117 size_t size_; |
| 120 PpapiGlobals* globals_; | |
| 121 }; | 118 }; |
| 122 | 119 |
| 123 // A thread that will increment and decrement the reference count of every var | 120 // A thread that will increment and decrement the reference count of every var |
| 124 // multiple times. | 121 // multiple times. |
| 125 class ChangeRefVarThreadDelegate : public base::PlatformThread::Delegate { | 122 class ChangeRefVarThreadDelegate : public base::PlatformThread::Delegate { |
| 126 public: | 123 public: |
| 127 ChangeRefVarThreadDelegate(const std::vector<PP_Var>& vars, | 124 ChangeRefVarThreadDelegate(const std::vector<PP_Var>& vars) : vars_(vars) { |
| 128 PpapiGlobals* globals) | |
| 129 : vars_(vars), globals_(globals) { | |
| 130 } | 125 } |
| 131 virtual ~ChangeRefVarThreadDelegate() {} | 126 virtual ~ChangeRefVarThreadDelegate() {} |
| 132 virtual void ThreadMain() { | 127 virtual void ThreadMain() { |
| 133 PpapiGlobals::SetPpapiGlobalsOnThreadForTest(globals_); | |
| 134 const PPB_Var* ppb_var = ppapi::PPB_Var_Shared::GetVarInterface1_1(); | 128 const PPB_Var* ppb_var = ppapi::PPB_Var_Shared::GetVarInterface1_1(); |
| 135 // Increment and decrement the reference count for each var kRefsToAdd | 129 // Increment and decrement the reference count for each var kRefsToAdd |
| 136 // times. Note that we always AddRef once before doing the matching Release, | 130 // times. Note that we always AddRef once before doing the matching Release, |
| 137 // to ensure that we never accidentally release the last reference. | 131 // to ensure that we never accidentally release the last reference. |
| 138 for (int ref = 0; ref < kRefsToAdd; ++ref) { | 132 for (int ref = 0; ref < kRefsToAdd; ++ref) { |
| 139 for (size_t i = 0; i < kNumStrings; ++i) { | 133 for (size_t i = 0; i < kNumStrings; ++i) { |
| 140 ppb_var->AddRef(vars_[i]); | 134 ppb_var->AddRef(vars_[i]); |
| 141 ppb_var->Release(vars_[i]); | 135 ppb_var->Release(vars_[i]); |
| 142 } | 136 } |
| 143 } | 137 } |
| 144 // Now add 1 ref to each Var. The net result is that all Vars will have a | 138 // 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 | 139 // ref-count of (kNumThreads + 1) after this. That will allow us to have all |
| 146 // threads release all vars later. | 140 // threads release all vars later. |
| 147 for (size_t i = 0; i < kNumStrings; ++i) { | 141 for (size_t i = 0; i < kNumStrings; ++i) { |
| 148 ppb_var->AddRef(vars_[i]); | 142 ppb_var->AddRef(vars_[i]); |
| 149 } | 143 } |
| 150 } | 144 } |
| 151 private: | 145 private: |
| 152 std::vector<PP_Var> vars_; | 146 std::vector<PP_Var> vars_; |
| 153 PpapiGlobals* globals_; | |
| 154 }; | 147 }; |
| 155 | 148 |
| 156 // A thread that will decrement the reference count of every var once. | 149 // A thread that will decrement the reference count of every var once. |
| 157 class RemoveRefVarThreadDelegate : public base::PlatformThread::Delegate { | 150 class RemoveRefVarThreadDelegate : public base::PlatformThread::Delegate { |
| 158 public: | 151 public: |
| 159 RemoveRefVarThreadDelegate(const std::vector<PP_Var>& vars, | 152 RemoveRefVarThreadDelegate(const std::vector<PP_Var>& vars) : vars_(vars) { |
| 160 PpapiGlobals* globals) | |
| 161 : vars_(vars), globals_(globals) { | |
| 162 } | 153 } |
| 163 virtual ~RemoveRefVarThreadDelegate() {} | 154 virtual ~RemoveRefVarThreadDelegate() {} |
| 164 virtual void ThreadMain() { | 155 virtual void ThreadMain() { |
| 165 PpapiGlobals::SetPpapiGlobalsOnThreadForTest(globals_); | |
| 166 const PPB_Var* ppb_var = ppapi::PPB_Var_Shared::GetVarInterface1_1(); | 156 const PPB_Var* ppb_var = ppapi::PPB_Var_Shared::GetVarInterface1_1(); |
| 167 for (size_t i = 0; i < kNumStrings; ++i) { | 157 for (size_t i = 0; i < kNumStrings; ++i) { |
| 168 ppb_var->Release(vars_[i]); | 158 ppb_var->Release(vars_[i]); |
| 169 } | 159 } |
| 170 } | 160 } |
| 171 private: | 161 private: |
| 172 std::vector<PP_Var> vars_; | 162 std::vector<PP_Var> vars_; |
| 173 PpapiGlobals* globals_; | |
| 174 }; | 163 }; |
| 175 | 164 |
| 176 } // namespace | 165 } // namespace |
| 177 | 166 |
| 178 #ifdef ENABLE_PEPPER_THREADING | 167 #ifdef ENABLE_PEPPER_THREADING |
| 179 TEST_F(PPB_VarTest, Threads) { | 168 TEST_F(PPB_VarTest, Threads) { |
| 180 #else | 169 #else |
| 181 TEST_F(PPB_VarTest, DISABLED_Threads) { | 170 TEST_F(PPB_VarTest, DISABLED_Threads) { |
| 182 #endif | 171 #endif |
| 183 std::vector<base::PlatformThreadHandle> create_var_threads(kNumThreads); | 172 std::vector<base::PlatformThreadHandle> create_var_threads(kNumThreads); |
| 184 std::vector<CreateVarThreadDelegate> create_var_delegates; | 173 std::vector<CreateVarThreadDelegate> create_var_delegates; |
| 185 // The strings that the threads will re-extract from Vars (so we can check | 174 // The strings that the threads will re-extract from Vars (so we can check |
| 186 // that they match the original strings). | 175 // that they match the original strings). |
| 187 std::vector<std::string> strings_out(kNumStrings); | 176 std::vector<std::string> strings_out(kNumStrings); |
| 188 size_t strings_per_thread = kNumStrings/kNumThreads; | 177 size_t strings_per_thread = kNumStrings/kNumThreads; |
| 189 // Give each thread an equal slice of strings to turn in to vars. (Except the | 178 // 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 | 179 // last thread may get fewer if kNumStrings is not evenly divisible by |
| 191 // kNumThreads). | 180 // kNumThreads). |
| 192 for (size_t slice_start= 0; slice_start < kNumStrings; | 181 for (size_t slice_start= 0; slice_start < kNumStrings; |
| 193 slice_start += strings_per_thread) { | 182 slice_start += strings_per_thread) { |
| 194 create_var_delegates.push_back( | 183 create_var_delegates.push_back( |
| 195 CreateVarThreadDelegate(pp_module(), | 184 CreateVarThreadDelegate(pp_module(), |
| 196 &test_strings_[slice_start], | 185 &test_strings_[slice_start], |
| 197 &vars_[slice_start], | 186 &vars_[slice_start], |
| 198 &strings_out[slice_start], | 187 &strings_out[slice_start], |
| 199 std::min(strings_per_thread, | 188 std::min(strings_per_thread, |
| 200 kNumStrings - slice_start), | 189 kNumStrings - slice_start))); |
| 201 GetGlobals())); | |
| 202 } | 190 } |
| 203 // Now run then join all the threads. | 191 // Now run then join all the threads. |
| 204 for (size_t i = 0; i < kNumThreads; ++i) | 192 for (size_t i = 0; i < kNumThreads; ++i) |
| 205 base::PlatformThread::Create(0, &create_var_delegates[i], | 193 base::PlatformThread::Create(0, &create_var_delegates[i], |
| 206 &create_var_threads[i]); | 194 &create_var_threads[i]); |
| 207 for (size_t i = 0; i < kNumThreads; ++i) | 195 for (size_t i = 0; i < kNumThreads; ++i) |
| 208 base::PlatformThread::Join(create_var_threads[i]); | 196 base::PlatformThread::Join(create_var_threads[i]); |
| 209 // Now check that the strings have the expected values. | 197 // Now check that the strings have the expected values. |
| 210 EXPECT_EQ(test_strings_, strings_out); | 198 EXPECT_EQ(test_strings_, strings_out); |
| 211 | 199 |
| 212 // Tinker with the reference counts in a multithreaded way. | 200 // Tinker with the reference counts in a multithreaded way. |
| 213 std::vector<base::PlatformThreadHandle> change_ref_var_threads(kNumThreads); | 201 std::vector<base::PlatformThreadHandle> change_ref_var_threads(kNumThreads); |
| 214 std::vector<ChangeRefVarThreadDelegate> change_ref_var_delegates; | 202 std::vector<ChangeRefVarThreadDelegate> change_ref_var_delegates; |
| 215 for (size_t i = 0; i < kNumThreads; ++i) | 203 for (size_t i = 0; i < kNumThreads; ++i) |
| 216 change_ref_var_delegates.push_back( | 204 change_ref_var_delegates.push_back(ChangeRefVarThreadDelegate(vars_)); |
| 217 ChangeRefVarThreadDelegate(vars_, GetGlobals())); | |
| 218 for (size_t i = 0; i < kNumThreads; ++i) { | 205 for (size_t i = 0; i < kNumThreads; ++i) { |
| 219 base::PlatformThread::Create(0, &change_ref_var_delegates[i], | 206 base::PlatformThread::Create(0, &change_ref_var_delegates[i], |
| 220 &change_ref_var_threads[i]); | 207 &change_ref_var_threads[i]); |
| 221 } | 208 } |
| 222 for (size_t i = 0; i < kNumThreads; ++i) | 209 for (size_t i = 0; i < kNumThreads; ++i) |
| 223 base::PlatformThread::Join(change_ref_var_threads[i]); | 210 base::PlatformThread::Join(change_ref_var_threads[i]); |
| 224 | 211 |
| 225 // Now each var has a refcount of (kNumThreads + 1). Let's decrement each var | 212 // 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 | 213 // 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. | 214 // when the last one removes a ref, the Var will be deleted. |
| 228 for (size_t i = 0; i < kNumStrings; ++i) { | 215 for (size_t i = 0; i < kNumStrings; ++i) { |
| 229 ppb_var_->Release(vars_[i]); | 216 ppb_var_->Release(vars_[i]); |
| 230 } | 217 } |
| 231 | 218 |
| 232 // Check that all vars are still valid and have the values we expect. | 219 // Check that all vars are still valid and have the values we expect. |
| 233 for (size_t i = 0; i < kNumStrings; ++i) | 220 for (size_t i = 0; i < kNumStrings; ++i) |
| 234 EXPECT_EQ(test_strings_[i], VarToString(vars_[i], ppb_var_)); | 221 EXPECT_EQ(test_strings_[i], VarToString(vars_[i], ppb_var_)); |
| 235 | 222 |
| 236 // Remove the last reference counts for all vars. | 223 // Remove the last reference counts for all vars. |
| 237 std::vector<base::PlatformThreadHandle> remove_ref_var_threads(kNumThreads); | 224 std::vector<base::PlatformThreadHandle> remove_ref_var_threads(kNumThreads); |
| 238 std::vector<RemoveRefVarThreadDelegate> remove_ref_var_delegates; | 225 std::vector<RemoveRefVarThreadDelegate> remove_ref_var_delegates; |
| 239 for (size_t i = 0; i < kNumThreads; ++i) | 226 for (size_t i = 0; i < kNumThreads; ++i) |
| 240 remove_ref_var_delegates.push_back( | 227 remove_ref_var_delegates.push_back(RemoveRefVarThreadDelegate(vars_)); |
| 241 RemoveRefVarThreadDelegate(vars_, GetGlobals())); | |
| 242 for (size_t i = 0; i < kNumThreads; ++i) { | 228 for (size_t i = 0; i < kNumThreads; ++i) { |
| 243 base::PlatformThread::Create(0, &remove_ref_var_delegates[i], | 229 base::PlatformThread::Create(0, &remove_ref_var_delegates[i], |
| 244 &remove_ref_var_threads[i]); | 230 &remove_ref_var_threads[i]); |
| 245 } | 231 } |
| 246 for (size_t i = 0; i < kNumThreads; ++i) | 232 for (size_t i = 0; i < kNumThreads; ++i) |
| 247 base::PlatformThread::Join(remove_ref_var_threads[i]); | 233 base::PlatformThread::Join(remove_ref_var_threads[i]); |
| 248 | 234 |
| 249 // All the vars should no longer represent valid strings. | 235 // All the vars should no longer represent valid strings. |
| 250 for (size_t i = 0; i < kNumStrings; ++i) { | 236 for (size_t i = 0; i < kNumStrings; ++i) { |
| 251 uint32_t len = 10; | 237 uint32_t len = 10; |
| 252 const char* utf8 = ppb_var_->VarToUtf8(vars_[i], &len); | 238 const char* utf8 = ppb_var_->VarToUtf8(vars_[i], &len); |
| 253 EXPECT_EQ(NULL, utf8); | 239 EXPECT_EQ(NULL, utf8); |
| 254 EXPECT_EQ(0u, len); | 240 EXPECT_EQ(0u, len); |
| 255 } | 241 } |
| 256 } | 242 } |
| 257 | 243 |
| 258 } // namespace proxy | 244 } // namespace proxy |
| 259 } // namespace ppapi | 245 } // namespace ppapi |
| OLD | NEW |