Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(46)

Side by Side Diff: media/audio/cross_process_notification_unittest.cc

Issue 9605015: Add a SharedMemSynchronizer class. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Address review comments Created 8 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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 TEST(CrossProcessNotification, ThousandNotifiersTwoThreads) {
251 const size_t kCount = 1000;
252 NotificationsOwner pairs(kCount);
253 size_t data[kCount] = {0};
254 // We use a multiple of the count so that the division in the check below
255 // will be nice and round.
256 size_t repeats = pairs.size() * 1;
257
258 MultiNotifierWorker worker_1(&data[0], repeats, &pairs.a());
259 MultiNotifierWorker worker_2(&data[0], repeats, &pairs.b());
260 base::PlatformThreadHandle thread_1, thread_2;
261 base::PlatformThread::Create(0, &worker_1, &thread_1);
262 base::PlatformThread::Create(0, &worker_2, &thread_2);
263
264 for (size_t i = 0; i < pairs.size(); ++i)
265 pairs.a()[i]->Signal();
266
267 base::PlatformThread::Join(thread_1);
268 base::PlatformThread::Join(thread_2);
269
270 size_t expected_total = pairs.size() * 2;
271 size_t total = 0;
272 for (size_t i = 0; i < pairs.size(); ++i) {
273 // The CrossProcessNotification::WaitForMultiple class should have ensured
274 // that all notifiers had the same quality of service.
275 EXPECT_EQ(expected_total / pairs.size(), data[i]);
276 total += data[i];
277 }
278 EXPECT_EQ(expected_total, total);
279 }
280
281 // Functionally equivalent (as far as the shared data goes) to the
282 // ThousandNotifiersTwoThreads test but uses a single pair of notifiers +
283 // FlagArray for the 1000 signals. This approach is significantly faster.
284 TEST(CrossProcessNotification, TwoNotifiersTwoThreads1000Signals) {
285 CrossProcessNotification a, b;
286 ASSERT_TRUE(CrossProcessNotification::InitializePair(&a, &b));
287
288 const size_t kCount = 1000;
289 FlagArray signals;
290 ASSERT_GE(signals.size(), kCount);
291 size_t data[kCount] = {0};
292
293 // Since this algorithm checks all events each time the notifier is
294 // signaled, |repeat| doesn't mean the same thing here as it does in
295 // ThousandNotifiersTwoThreads. 1 repeat here is the same as kCount
296 // repeats in ThousandNotifiersTwoThreads.
297 size_t repeats = 1;
298 MultiNotifierWorkerFlagArray worker1(kCount, &signals, &data[0], repeats, &a);
299 MultiNotifierWorkerFlagArray worker2(kCount, &signals, &data[0], repeats, &b);
300 base::PlatformThreadHandle thread1, thread2;
301 base::PlatformThread::Create(0, &worker1, &thread1);
302 base::PlatformThread::Create(0, &worker2, &thread2);
303
304 for (size_t i = 0; i < kCount; ++i)
305 signals.set(i);
306 a.Signal();
307
308 base::PlatformThread::Join(thread1);
309 base::PlatformThread::Join(thread2);
310
311 size_t expected_total = kCount * 2;
312 size_t total = 0;
313 for (size_t i = 0; i < kCount; ++i) {
314 // Since for each signal, we process all signaled events, the shared data
315 // variables should all be equal.
316 EXPECT_EQ(expected_total / kCount, data[i]);
317 total += data[i];
318 }
319 EXPECT_EQ(expected_total, total);
320 }
321
322 // Test the maximum number of notifiers without spinning further wait
323 // threads on Windows. This test assumes we can always create 64 pairs and
324 // bails if we can't.
325 TEST(CrossProcessNotification, MultipleWaits64) {
326 const size_t kCount = 64;
327 NotificationsOwner pairs(kCount);
328 ASSERT_TRUE(pairs.size() == kCount);
329
330 CrossProcessNotification::WaitForMultiple waiter(&pairs.b());
331 for (size_t i = 0; i < kCount; ++i) {
332 pairs.a()[i]->Signal();
333 int index = waiter.Wait();
334 EXPECT_EQ(i, static_cast<size_t>(index));
335 }
336 }
337
338 // Tests waiting for more notifiers than the OS supports on one thread.
339 // The test will create at most 1000 pairs, but on mac/linux bots the actual
340 // number will be lower. See comment about the RLIMIT_NOFILE limit above for
341 // more details.
342 TEST(CrossProcessNotification, MultipleWaits1000) {
343 // A 1000 notifiers requires 16 threads on Windows, including the current
344 // one, to perform the wait operation.
345 const size_t kCount = 1000;
346 NotificationsOwner pairs(kCount);
347
348 for (size_t i = 0; i < pairs.size(); ++i) {
349 pairs.a()[i]->Signal();
350 // To disable the load distribution algorithm and force the extra worker
351 // thread(s) to catch the signaled event, we define the |waiter| inside
352 // the loop.
353 CrossProcessNotification::WaitForMultiple waiter(&pairs.b());
354 int index = waiter.Wait();
355 EXPECT_EQ(i, static_cast<size_t>(index));
356 }
357 }
358
359 class CrossProcessNotificationMultiProcessTest : public base::MultiProcessTest {
360 public:
361 static const char kSharedMemName[];
362 static const size_t kSharedMemSize = 1024;
363
364 protected:
365 virtual void SetUp() OVERRIDE {
366 base::MultiProcessTest::SetUp();
367 }
368
369 virtual void TearDown() OVERRIDE {
370 base::MultiProcessTest::TearDown();
371 }
372 };
373
374 // static
375 const char CrossProcessNotificationMultiProcessTest::kSharedMemName[] =
376 "CrossProcessNotificationMultiProcessTest";
377
378 namespace {
379 // A very crude IPC mechanism that we use to set up the spawned child process
380 // and the parent process.
381 struct CrudeIpc {
382 uint8 ready;
383 CrossProcessNotification::IPCHandle handle_1;
384 CrossProcessNotification::IPCHandle handle_2;
385 };
386 } // end namespace
387
388 // The main routine of the child process. Waits for the parent process
389 // to copy handles over to the child and then uses a CrossProcessNotification to
390 // wait and signal to the parent process.
391 MULTIPROCESS_TEST_MAIN(CrossProcessNotificationChildMain) {
392 base::SharedMemory mem;
393 bool ok = mem.CreateNamed(
394 CrossProcessNotificationMultiProcessTest::kSharedMemName,
395 true,
396 CrossProcessNotificationMultiProcessTest::kSharedMemSize);
397 DCHECK(ok);
398 if (!ok) {
399 LOG(ERROR) << "Failed to open shared memory segment.";
400 return -1;
401 }
402
403 mem.Map(CrossProcessNotificationMultiProcessTest::kSharedMemSize);
404 CrudeIpc* ipc = reinterpret_cast<CrudeIpc*>(mem.memory());
405
406 while (!ipc->ready)
407 base::PlatformThread::Sleep(10);
408
409 CrossProcessNotification notifier(ipc->handle_1, ipc->handle_2);
410 notifier.Wait();
411 notifier.Signal();
412
413 return 0;
414 }
415
416 // Spawns a new process and hands a CrossProcessNotification instance to the
417 // new process. Once that's done, it waits for the child process to signal
418 // it's end and quits.
419 TEST_F(CrossProcessNotificationMultiProcessTest, Basic) {
420 base::SharedMemory mem;
421 mem.Delete(kSharedMemName); // In case a previous run was unsuccessful.
422 bool ok = mem.CreateNamed(kSharedMemName, false, kSharedMemSize);
423 ASSERT_TRUE(ok);
424
425 ASSERT_TRUE(mem.Map(kSharedMemSize));
426
427 CrossProcessNotification a, b;
428 ASSERT_TRUE(CrossProcessNotification::InitializePair(&a, &b));
429 EXPECT_TRUE(a.IsValid());
430 EXPECT_TRUE(b.IsValid());
431
432 CrudeIpc* ipc = reinterpret_cast<CrudeIpc*>(mem.memory());
433 ipc->ready = false;
434
435 #if defined(OS_POSIX)
436 const int kPosixChildSocket = 20;
437 EXPECT_TRUE(b.ShareToProcess(NULL, &ipc->handle_1, &ipc->handle_2));
438 base::FileHandleMappingVector fd_mapping_vec;
439 fd_mapping_vec.push_back(std::pair<int, int>(ipc->handle_1.fd,
440 kPosixChildSocket));
441 ipc->handle_1.fd = kPosixChildSocket;
442 base::ProcessHandle process = SpawnChild("CrossProcessNotificationChildMain",
443 fd_mapping_vec, false);
444 #else
445 base::ProcessHandle process = SpawnChild("CrossProcessNotificationChildMain",
446 false);
447 EXPECT_TRUE(b.ShareToProcess(process, &ipc->handle_1, &ipc->handle_2));
448 #endif
449
450 ipc->ready = true;
451
452 a.Signal();
453 a.Wait();
454
455 int exit_code = -1;
456 base::WaitForExitCode(process, &exit_code);
457 EXPECT_EQ(0, exit_code);
458 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698