OLD | NEW |
(Empty) | |
| 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 "base/compiler_specific.h" |
| 6 #include "base/logging.h" |
| 7 #include "base/shared_memory.h" |
| 8 #include "base/test/multiprocess_test.h" |
| 9 #include "base/threading/platform_thread.h" |
| 10 #include "media/audio/shared_mem_synchronizer.h" |
| 11 #include "testing/gtest/include/gtest/gtest.h" |
| 12 #include "testing/multiprocess_func_list.h" |
| 13 |
| 14 namespace { |
| 15 // A simple thread that we'll run two instances of. Both threads get |
| 16 // a pointer to the same |shared_data| and use a SharedMemSynchronizer to |
| 17 // control when each thread can read/write. |
| 18 class SingleSynchronizerWorker : public base::PlatformThread::Delegate { |
| 19 public: |
| 20 SingleSynchronizerWorker(size_t* shared_data, size_t repeats, |
| 21 SharedMemSynchronizer* synchronizer) |
| 22 : shared_data_(shared_data), repeats_(repeats), |
| 23 synchronizer_(synchronizer) { |
| 24 } |
| 25 virtual ~SingleSynchronizerWorker() {} |
| 26 |
| 27 virtual void ThreadMain() OVERRIDE { |
| 28 for (size_t i = 0; i < repeats_; ++i) { |
| 29 synchronizer_->Wait(); |
| 30 ++(*shared_data_); |
| 31 synchronizer_->Signal(); |
| 32 } |
| 33 } |
| 34 |
| 35 private: |
| 36 size_t* shared_data_; |
| 37 size_t repeats_; |
| 38 SharedMemSynchronizer* synchronizer_; |
| 39 DISALLOW_COPY_AND_ASSIGN(SingleSynchronizerWorker); |
| 40 }; |
| 41 |
| 42 // Similar to SingleSynchronizerWorker, except each instance of this class will |
| 43 // have >1 instances of SharedMemSynchronizer to Wait/Signal and an equal amount |
| 44 // of |shared_data| that the synchronizers control access to. |
| 45 class MultiSynchronizerWorker : public base::PlatformThread::Delegate { |
| 46 public: |
| 47 MultiSynchronizerWorker(size_t* shared_data, size_t repeats, |
| 48 SharedMemSynchronizer* synchronizers, |
| 49 size_t count) |
| 50 : shared_data_(shared_data), repeats_(repeats), |
| 51 synchronizers_(synchronizers), count_(count) { |
| 52 } |
| 53 virtual ~MultiSynchronizerWorker() {} |
| 54 |
| 55 virtual void ThreadMain() OVERRIDE { |
| 56 SharedMemSynchronizer::WaitForMultiple waiter(synchronizers_, count_); |
| 57 for (size_t i = 0; i < repeats_; ++i) { |
| 58 int signaled = waiter.Wait(); |
| 59 ++shared_data_[signaled]; |
| 60 synchronizers_[signaled].Signal(); |
| 61 } |
| 62 } |
| 63 |
| 64 private: |
| 65 size_t* shared_data_; |
| 66 size_t repeats_; |
| 67 SharedMemSynchronizer* synchronizers_; |
| 68 size_t count_; |
| 69 DISALLOW_COPY_AND_ASSIGN(MultiSynchronizerWorker); |
| 70 }; |
| 71 |
| 72 // A fixed array of bool flags. Each flag uses 1 bit. Use sizeof(FlagArray) |
| 73 // to determine how much memory you need. The number of flags will therefore |
| 74 // be sizeof(FlagArray) * 8. |
| 75 // We use 'struct' to signify that this structures represents compiler |
| 76 // independent structured data. I.e. you must be able to map this class |
| 77 // to a piece of shared memory of size sizeof(FlagArray) and be able to |
| 78 // use the class. No vtables etc. |
| 79 // TODO(tommi): Move this to its own header when we start using it for signaling |
| 80 // audio devices. As is, it's just here for perf comparison against the |
| 81 // "multiple synchronizers" approach. |
| 82 struct FlagArray { |
| 83 public: |
| 84 FlagArray() : flags_() {} |
| 85 |
| 86 bool is_set(size_t index) const { |
| 87 return (flags_[index >> 5] & (1 << (index & 31))) ? true : false; |
| 88 } |
| 89 |
| 90 void set(size_t index) { |
| 91 flags_[index >> 5] |= (1U << (static_cast<uint32>(index) & 31)); |
| 92 } |
| 93 |
| 94 void clear(size_t index) { |
| 95 flags_[index >> 5] &= ~(1U << (static_cast<uint32>(index) & 31)); |
| 96 } |
| 97 |
| 98 // Returns the number of flags that can be set/checked. |
| 99 size_t size() const { return sizeof(flags_) * 8; } |
| 100 |
| 101 private: |
| 102 // 256 * 32 = 8192 flags in 1KB. |
| 103 uint32 flags_[256]; |
| 104 DISALLOW_COPY_AND_ASSIGN(FlagArray); |
| 105 }; |
| 106 |
| 107 class MultiSynchronizerWorkerFlagArray : public base::PlatformThread::Delegate { |
| 108 public: |
| 109 MultiSynchronizerWorkerFlagArray(size_t count, FlagArray* signals, |
| 110 size_t* shared_data, size_t repeats, |
| 111 SharedMemSynchronizer* synchronizer) |
| 112 : count_(count), signals_(signals), shared_data_(shared_data), |
| 113 repeats_(repeats), synchronizer_(synchronizer) { |
| 114 } |
| 115 virtual ~MultiSynchronizerWorkerFlagArray() {} |
| 116 |
| 117 virtual void ThreadMain() OVERRIDE { |
| 118 for (size_t i = 0; i < repeats_; ++i) { |
| 119 synchronizer_->Wait(); |
| 120 for (size_t s = 0; s < count_; ++s) { |
| 121 if (signals_->is_set(s)) { |
| 122 ++shared_data_[s]; |
| 123 // We don't clear the flag here but simply leave it signaled because |
| 124 // we want the other thread to also increment this variable. |
| 125 } |
| 126 } |
| 127 synchronizer_->Signal(); |
| 128 } |
| 129 } |
| 130 |
| 131 private: |
| 132 size_t count_; |
| 133 FlagArray* signals_; |
| 134 size_t* shared_data_; |
| 135 size_t repeats_; |
| 136 SharedMemSynchronizer* synchronizer_; |
| 137 DISALLOW_COPY_AND_ASSIGN(MultiSynchronizerWorkerFlagArray); |
| 138 }; |
| 139 |
| 140 } // end namespace |
| 141 |
| 142 TEST(SharedMemSynchronizer, FlagArray) { |
| 143 FlagArray flags; |
| 144 EXPECT_GT(flags.size(), 1000U); |
| 145 for (size_t i = 0; i < flags.size(); ++i) { |
| 146 EXPECT_FALSE(flags.is_set(i)); |
| 147 flags.set(i); |
| 148 EXPECT_TRUE(flags.is_set(i)); |
| 149 flags.clear(i); |
| 150 EXPECT_FALSE(flags.is_set(i)); |
| 151 } |
| 152 } |
| 153 |
| 154 // Initializes two synchronizers, signals the each one and make sure the others |
| 155 // wait is satisfied. |
| 156 TEST(SharedMemSynchronizer, Basic) { |
| 157 SharedMemSynchronizer a, b; |
| 158 ASSERT_TRUE(SharedMemSynchronizer::InitializePair(&a, &b)); |
| 159 EXPECT_TRUE(a.IsValid()); |
| 160 EXPECT_TRUE(b.IsValid()); |
| 161 |
| 162 a.Signal(); |
| 163 b.Wait(); |
| 164 |
| 165 b.Signal(); |
| 166 a.Wait(); |
| 167 } |
| 168 |
| 169 // Spins two worker threads, each with their own SharedMemSynchronizer that they |
| 170 // use to read and write from a shared memory buffer. |
| 171 TEST(SharedMemSynchronizer, TwoThreads) { |
| 172 SharedMemSynchronizer a, b; |
| 173 ASSERT_TRUE(SharedMemSynchronizer::InitializePair(&a, &b)); |
| 174 |
| 175 size_t data = 0; |
| 176 const size_t kRepeats = 10000; |
| 177 SingleSynchronizerWorker worker_1(&data, kRepeats, &a); |
| 178 SingleSynchronizerWorker worker_2(&data, kRepeats, &b); |
| 179 base::PlatformThreadHandle thread_1, thread_2; |
| 180 base::PlatformThread::Create(0, &worker_1, &thread_1); |
| 181 base::PlatformThread::Create(0, &worker_2, &thread_2); |
| 182 |
| 183 // Start the first thread. They should ping pong a few times and take turns |
| 184 // incrementing the shared variable and never step on each other's toes. |
| 185 a.Signal(); |
| 186 |
| 187 base::PlatformThread::Join(thread_1); |
| 188 base::PlatformThread::Join(thread_2); |
| 189 |
| 190 EXPECT_EQ(kRepeats * 2, data); |
| 191 } |
| 192 |
| 193 // Uses a pair of threads to access up to 1000 pieces of synchronized shared |
| 194 // data. On regular dev machines, the number of synchronizers should be 1000, |
| 195 // but on mac and linux bots, the number will be smaller due to the |
| 196 // RLIMIT_NOFILE limit. Specifically, linux will have this limit at 1024 which |
| 197 // means for this test that the max number of synchronizers will be in the |
| 198 // range 500-512. On Mac the limit is 256, so |count| will be ~120. Oh, and |
| 199 // raising the limit via setrlimit() won't work. |
| 200 TEST(SharedMemSynchronizer, ThousandSynchronizersTwoThreads) { |
| 201 const size_t kCount = 1000; |
| 202 SharedMemSynchronizer a[kCount], b[kCount]; |
| 203 size_t count = 0; |
| 204 for (size_t i = 0; i < kCount; ++i) { |
| 205 if (!SharedMemSynchronizer::InitializePair(&a[i], &b[i])) { |
| 206 LOG(WARNING) << "SharedMemSynchronizer::InitializePair failed at " << i; |
| 207 break; |
| 208 } |
| 209 ++count; |
| 210 } |
| 211 |
| 212 size_t data[kCount] = {0}; |
| 213 // We use a multiple of the count so that the division in the check below |
| 214 // will be nice and round. |
| 215 size_t repeats = count * 1; |
| 216 MultiSynchronizerWorker worker_1(&data[0], repeats, &a[0], count); |
| 217 MultiSynchronizerWorker worker_2(&data[0], repeats, &b[0], count); |
| 218 base::PlatformThreadHandle thread_1, thread_2; |
| 219 base::PlatformThread::Create(0, &worker_1, &thread_1); |
| 220 base::PlatformThread::Create(0, &worker_2, &thread_2); |
| 221 |
| 222 for (size_t i = 0; i < count; ++i) |
| 223 a[i].Signal(); |
| 224 |
| 225 base::PlatformThread::Join(thread_1); |
| 226 base::PlatformThread::Join(thread_2); |
| 227 |
| 228 size_t expected_total = count * 2; |
| 229 size_t total = 0; |
| 230 for (size_t i = 0; i < count; ++i) { |
| 231 // The SharedMemSynchronizer::WaitForMultiple class should have ensured that |
| 232 // all synchronizers had the same quality of service. |
| 233 EXPECT_EQ(expected_total / count, data[i]); |
| 234 total += data[i]; |
| 235 } |
| 236 EXPECT_EQ(expected_total, total); |
| 237 } |
| 238 |
| 239 // Functionally equivalent (as far as the shared data goes) to the |
| 240 // ThousandSynchronizersTwoThreads test but uses a single pair of |
| 241 // synchronizers + FlagArray for the 1000 signals. |
| 242 // This approach is significantly faster. |
| 243 TEST(SharedMemSynchronizer, TwoSynchronizersTwoThreads1000Signals) { |
| 244 SharedMemSynchronizer a, b; |
| 245 ASSERT_TRUE(SharedMemSynchronizer::InitializePair(&a, &b)); |
| 246 |
| 247 const size_t kCount = 1000; |
| 248 FlagArray signals; |
| 249 ASSERT_GE(signals.size(), kCount); |
| 250 size_t data[kCount] = {0}; |
| 251 |
| 252 // Since this algorithm checks all events each time the synchronizer is |
| 253 // signaled, |repeat| doesn't mean the same thing here as it does in |
| 254 // ThousandsynchronizersTwoThreads. 1 repeat here is the same as kCount |
| 255 // repeats in ThousandsynchronizersTwoThreads. |
| 256 size_t repeats = 1; |
| 257 MultiSynchronizerWorkerFlagArray worker_1( |
| 258 kCount, &signals, &data[0], repeats, &a); |
| 259 MultiSynchronizerWorkerFlagArray worker_2( |
| 260 kCount, &signals, &data[0], repeats, &b); |
| 261 base::PlatformThreadHandle thread_1, thread_2; |
| 262 base::PlatformThread::Create(0, &worker_1, &thread_1); |
| 263 base::PlatformThread::Create(0, &worker_2, &thread_2); |
| 264 |
| 265 for (size_t i = 0; i < kCount; ++i) |
| 266 signals.set(i); |
| 267 a.Signal(); |
| 268 |
| 269 base::PlatformThread::Join(thread_1); |
| 270 base::PlatformThread::Join(thread_2); |
| 271 |
| 272 size_t expected_total = kCount * 2; |
| 273 size_t total = 0; |
| 274 for (size_t i = 0; i < kCount; ++i) { |
| 275 // Since for each signal, we process all signaled events, the shared data |
| 276 // variables should all be equal. |
| 277 EXPECT_EQ(expected_total / kCount, data[i]); |
| 278 total += data[i]; |
| 279 } |
| 280 EXPECT_EQ(expected_total, total); |
| 281 } |
| 282 |
| 283 // Test the maximum number of synchronizers without spinning further wait |
| 284 // threads on Windows. This test assumes we can always create 64 pairs and |
| 285 // bails if we can't. |
| 286 TEST(SharedMemSynchronizer, MultipleWaits64) { |
| 287 const size_t kCount = 64; |
| 288 SharedMemSynchronizer a[kCount], b[kCount]; |
| 289 for (size_t i = 0; i < kCount; ++i) { |
| 290 ASSERT_TRUE(SharedMemSynchronizer::InitializePair(&a[i], &b[i])); |
| 291 } |
| 292 |
| 293 SharedMemSynchronizer::WaitForMultiple waiter(&b[0], kCount); |
| 294 for (size_t i = 0; i < kCount; ++i) { |
| 295 a[i].Signal(); |
| 296 int index = waiter.Wait(); |
| 297 EXPECT_EQ(i, static_cast<size_t>(index)); |
| 298 } |
| 299 } |
| 300 |
| 301 // Tests waiting for more synchronizers than the OS supports on one thread. |
| 302 // The test will create at most 1000 pairs, but on mac/linux bots the actual |
| 303 // number will be lower. See comment about the RLIMIT_NOFILE limit above for |
| 304 // more details. |
| 305 TEST(SharedMemSynchronizer, MultipleWaits1000) { |
| 306 // A 1000 synchronizers requires 16 threads on Windows, including the current |
| 307 // one, to perform the wait operation. |
| 308 const size_t kCount = 1000; |
| 309 SharedMemSynchronizer a[kCount], b[kCount]; |
| 310 size_t count = 0; |
| 311 for (size_t i = 0; i < kCount; ++i) { |
| 312 if (!SharedMemSynchronizer::InitializePair(&a[i], &b[i])) { |
| 313 LOG(WARNING) << "SharedMemSynchronizer::InitializePair failed at " << i; |
| 314 break; |
| 315 } |
| 316 ++count; |
| 317 } |
| 318 |
| 319 for (size_t i = 0; i < count; ++i) { |
| 320 a[i].Signal(); |
| 321 // To disable the load distribution algorithm and force the extra worker |
| 322 // thread(s) to catch the signaled event, we define the |waiter| inside |
| 323 // the loop. |
| 324 SharedMemSynchronizer::WaitForMultiple waiter(&b[0], count); |
| 325 int index = waiter.Wait(); |
| 326 EXPECT_EQ(i, static_cast<size_t>(index)); |
| 327 } |
| 328 } |
| 329 |
| 330 class SharedMemSynchronizerMultiProcessTest : public base::MultiProcessTest { |
| 331 public: |
| 332 static const char kSharedMemName[]; |
| 333 static const size_t kSharedMemSize = 1024; |
| 334 |
| 335 protected: |
| 336 virtual void SetUp() OVERRIDE { |
| 337 base::MultiProcessTest::SetUp(); |
| 338 } |
| 339 |
| 340 virtual void TearDown() OVERRIDE { |
| 341 base::MultiProcessTest::TearDown(); |
| 342 } |
| 343 }; |
| 344 |
| 345 // static |
| 346 const char SharedMemSynchronizerMultiProcessTest::kSharedMemName[] = |
| 347 "SharedMemSynchronizerMultiProcessTest"; |
| 348 |
| 349 namespace { |
| 350 // A very crude IPC mechanism that we use to set up the spawned child process |
| 351 // and the parent process. |
| 352 struct CrudeIpc { |
| 353 uint8 ready; |
| 354 SharedMemSynchronizer::IPCHandle handle_1; |
| 355 SharedMemSynchronizer::IPCHandle handle_2; |
| 356 }; |
| 357 } // end namespace |
| 358 |
| 359 // The main routine of the child process. Waits for the parent process |
| 360 // to copy handles over to the child and then uses a SharedMemSynchronizer to |
| 361 // wait and signal to the parent process. |
| 362 MULTIPROCESS_TEST_MAIN(SharedMemSynchronizerChildMain) { |
| 363 base::SharedMemory mem; |
| 364 bool ok = mem.CreateNamed( |
| 365 SharedMemSynchronizerMultiProcessTest::kSharedMemName, |
| 366 true, |
| 367 SharedMemSynchronizerMultiProcessTest::kSharedMemSize); |
| 368 DCHECK(ok); |
| 369 if (!ok) { |
| 370 LOG(ERROR) << "Failed to open shared memory segment."; |
| 371 return -1; |
| 372 } |
| 373 |
| 374 mem.Map(SharedMemSynchronizerMultiProcessTest::kSharedMemSize); |
| 375 CrudeIpc* ipc = reinterpret_cast<CrudeIpc*>(mem.memory()); |
| 376 |
| 377 while (!ipc->ready) |
| 378 base::PlatformThread::Sleep(10); |
| 379 |
| 380 SharedMemSynchronizer synchronizer(ipc->handle_1, ipc->handle_2); |
| 381 synchronizer.Wait(); |
| 382 synchronizer.Signal(); |
| 383 |
| 384 return 0; |
| 385 } |
| 386 |
| 387 // Spawns a new process and hands a SharedMemSynchronizer instance to the |
| 388 // new process. Once that's done, it waits for the child process to signal |
| 389 // it's end and quits. |
| 390 TEST_F(SharedMemSynchronizerMultiProcessTest, Basic) { |
| 391 base::SharedMemory mem; |
| 392 mem.Delete(kSharedMemName); // In case a previous run was unsuccessful. |
| 393 bool ok = mem.CreateNamed(kSharedMemName, false, kSharedMemSize); |
| 394 ASSERT_TRUE(ok); |
| 395 |
| 396 ASSERT_TRUE(mem.Map(kSharedMemSize)); |
| 397 |
| 398 SharedMemSynchronizer a, b; |
| 399 ASSERT_TRUE(SharedMemSynchronizer::InitializePair(&a, &b)); |
| 400 EXPECT_TRUE(a.IsValid()); |
| 401 EXPECT_TRUE(b.IsValid()); |
| 402 |
| 403 CrudeIpc* ipc = reinterpret_cast<CrudeIpc*>(mem.memory()); |
| 404 ipc->ready = false; |
| 405 |
| 406 #if defined(OS_POSIX) |
| 407 const int kPosixChildSocket = 20; |
| 408 EXPECT_TRUE(b.ShareToProcess(NULL, &ipc->handle_1, &ipc->handle_2)); |
| 409 base::FileHandleMappingVector fd_mapping_vec; |
| 410 fd_mapping_vec.push_back(std::pair<int, int>(ipc->handle_1.fd, |
| 411 kPosixChildSocket)); |
| 412 ipc->handle_1.fd = kPosixChildSocket; |
| 413 base::ProcessHandle process = SpawnChild("SharedMemSynchronizerChildMain", |
| 414 fd_mapping_vec, false); |
| 415 #else |
| 416 base::ProcessHandle process = SpawnChild("SharedMemSynchronizerChildMain", |
| 417 false); |
| 418 EXPECT_TRUE(b.ShareToProcess(process, &ipc->handle_1, &ipc->handle_2)); |
| 419 #endif |
| 420 |
| 421 ipc->ready = true; |
| 422 |
| 423 a.Signal(); |
| 424 a.Wait(); |
| 425 |
| 426 int exit_code = -1; |
| 427 base::WaitForExitCode(process, &exit_code); |
| 428 EXPECT_EQ(0, exit_code); |
| 429 } |
OLD | NEW |