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

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: Disabled two tests before relanding 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 // 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 }
OLDNEW
« no previous file with comments | « media/audio/cross_process_notification_posix.cc ('k') | media/audio/cross_process_notification_win.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698