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