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