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/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 } | |
OLD | NEW |