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

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

Issue 9605015: Add a SharedMemSynchronizer class. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: 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/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 {
scherkus (not reviewing) 2012/03/08 02:37:08 AFAIK std::vector<bool> is implemented as a bit ar
tommi (sloooow) - chröme 2012/03/08 16:10:57 I did consider that but the difference is this req
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::Create(&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::Create(&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::Create(&a[i], &b[i])) {
206 LOG(WARNING) << "SharedMemSynchronizer::Create 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::Create(&a, &b));
246
247 const size_t kCount = 1000;
248 FlagArray signals;
249 ASSERT_TRUE(signals.size() >= kCount);
scherkus (not reviewing) 2012/03/08 02:37:08 assert_ge?
tommi (sloooow) - chröme 2012/03/08 16:10:57 Done.
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 = /*kCount * */ 1;
scherkus (not reviewing) 2012/03/08 02:37:08 commented out code?
tommi (sloooow) - chröme 2012/03/08 16:10:57 Removed. I thought this would help make the comme
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::Create(&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::Create(&a[i], &b[i])) {
313 LOG(WARNING) << "SharedMemSynchronizer::Create 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::Create(&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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698