1 #include "libbroadcastring/broadcast_ring.h"
2
3 #include <stdlib.h>
4 #include <memory>
5 #include <thread> // NOLINT
6 #include <sys/mman.h>
7
8 #include <gtest/gtest.h>
9
10 namespace android {
11 namespace dvr {
12 namespace {
13
14 template <uint32_t N>
15 struct alignas(8) Aligned {
16 char v[N];
17 };
18
19 template <uint32_t N>
20 struct alignas(8) Sized {
Sizedandroid::dvr::__anon358b6f730111::Sized21 Sized() { Clear(); }
Sizedandroid::dvr::__anon358b6f730111::Sized22 explicit Sized(char c) { Fill(c); }
23 char v[sizeof(Aligned<N>)];
Clearandroid::dvr::__anon358b6f730111::Sized24 void Clear() { memset(v, 0, sizeof(v)); }
Fillandroid::dvr::__anon358b6f730111::Sized25 void Fill(char c) { memset(v, c, sizeof(v)); }
Patternandroid::dvr::__anon358b6f730111::Sized26 static Sized Pattern(uint8_t c) {
27 Sized sized;
28 for (size_t i = 0; i < sizeof(v); ++i) {
29 sized.v[i] = static_cast<char>(c + i);
30 }
31 return sized;
32 }
operator ==android::dvr::__anon358b6f730111::Sized33 bool operator==(const Sized& right) const {
34 static_assert(sizeof(*this) == sizeof(v), "Size mismatch");
35 return !memcmp(v, right.v, sizeof(v));
36 }
37 template <typename SmallerSized>
Truncateandroid::dvr::__anon358b6f730111::Sized38 SmallerSized Truncate() const {
39 SmallerSized val;
40 static_assert(sizeof(val.v) <= sizeof(v), "Cannot truncate to larger size");
41 memcpy(val.v, v, sizeof(val.v));
42 return val;
43 }
44 };
45
FillChar(int val)46 char FillChar(int val) { return static_cast<char>(val); }
47
48 struct FakeMmap {
FakeMmapandroid::dvr::__anon358b6f730111::FakeMmap49 explicit FakeMmap(size_t size) : size(size), data(new char[size]) {}
50 size_t size;
51 std::unique_ptr<char[]> data;
mmapandroid::dvr::__anon358b6f730111::FakeMmap52 void* mmap() { return static_cast<void*>(data.get()); }
53 };
54
55 template <typename Ring>
CreateRing(Ring * ring,uint32_t count)56 FakeMmap CreateRing(Ring* ring, uint32_t count) {
57 FakeMmap mmap(Ring::MemorySize(count));
58 *ring = Ring::Create(mmap.mmap(), mmap.size, count);
59 return mmap;
60 }
61
62 template <typename RecordType, bool StaticSize = false,
63 uint32_t StaticCount = 0, uint32_t MaxReserved = 1,
64 uint32_t MinAvailable = 0>
65 struct Traits {
66 using Record = RecordType;
67 static constexpr bool kUseStaticRecordSize = StaticSize;
68 static constexpr uint32_t kStaticRecordCount = StaticCount;
69 static constexpr uint32_t kMaxReservedRecords = MaxReserved;
70 static constexpr uint32_t kMinAvailableRecords = MinAvailable;
71 static constexpr uint32_t kMinRecordCount = MaxReserved + MinAvailable;
72 };
73
74 template <typename Record, bool StaticSize = false, uint32_t MaxReserved = 1,
75 uint32_t MinAvailable = 7>
76 struct TraitsDynamic
77 : public Traits<Record, StaticSize, 0, MaxReserved, MinAvailable> {
78 using Ring = BroadcastRing<Record, TraitsDynamic>;
MinCountandroid::dvr::__anon358b6f730111::TraitsDynamic79 static uint32_t MinCount() { return MaxReserved + MinAvailable; }
80 };
81
82 template <typename Record, uint32_t StaticCount = 1, bool StaticSize = true,
83 uint32_t MaxReserved = 1, uint32_t MinAvailable = 0>
84 struct TraitsStatic
85 : public Traits<Record, true, StaticCount, MaxReserved, MinAvailable> {
86 using Ring = BroadcastRing<Record, TraitsStatic>;
MinCountandroid::dvr::__anon358b6f730111::TraitsStatic87 static uint32_t MinCount() { return StaticCount; }
88 };
89
90 using Dynamic_8_NxM = TraitsDynamic<Sized<8>>;
91 using Dynamic_16_NxM = TraitsDynamic<Sized<16>>;
92 using Dynamic_32_NxM = TraitsDynamic<Sized<32>>;
93 using Dynamic_32_32xM = TraitsDynamic<Sized<32>, true>;
94 using Dynamic_16_NxM_1plus0 = TraitsDynamic<Sized<16>, false, 1, 0>;
95 using Dynamic_16_NxM_1plus1 = TraitsDynamic<Sized<16>, false, 1, 1>;
96 using Dynamic_16_NxM_5plus11 = TraitsDynamic<Sized<16>, false, 5, 11>;
97 using Dynamic_256_NxM_1plus0 = TraitsDynamic<Sized<256>, false, 1, 0>;
98
99 using Static_8_8x1 = TraitsStatic<Sized<8>, 1>;
100 using Static_8_8x16 = TraitsStatic<Sized<8>, 16>;
101 using Static_16_16x8 = TraitsStatic<Sized<16>, 8>;
102 using Static_16_16x16 = TraitsStatic<Sized<16>, 16>;
103 using Static_16_16x32 = TraitsStatic<Sized<16>, 32>;
104 using Static_32_Nx8 = TraitsStatic<Sized<32>, 8, false>;
105
106 using TraitsList = ::testing::Types<Dynamic_8_NxM, //
107 Dynamic_16_NxM, //
108 Dynamic_32_NxM, //
109 Dynamic_32_32xM, //
110 Dynamic_16_NxM_1plus0, //
111 Dynamic_16_NxM_1plus1, //
112 Dynamic_16_NxM_5plus11, //
113 Dynamic_256_NxM_1plus0, //
114 Static_8_8x1, //
115 Static_8_8x16, //
116 Static_16_16x8, //
117 Static_16_16x16, //
118 Static_16_16x32, //
119 Static_32_Nx8>;
120
121 } // namespace
122
123 template <typename T>
124 class BroadcastRingTest : public ::testing::Test {};
125
126 TYPED_TEST_CASE(BroadcastRingTest, TraitsList);
127
TYPED_TEST(BroadcastRingTest,Geometry)128 TYPED_TEST(BroadcastRingTest, Geometry) {
129 using Record = typename TypeParam::Record;
130 using Ring = typename TypeParam::Ring;
131 Ring ring;
132 auto mmap = CreateRing(&ring, Ring::Traits::MinCount());
133 EXPECT_EQ(Ring::Traits::MinCount(), ring.record_count());
134 EXPECT_EQ(sizeof(Record), ring.record_size());
135 }
136
TYPED_TEST(BroadcastRingTest,PutGet)137 TYPED_TEST(BroadcastRingTest, PutGet) {
138 using Record = typename TypeParam::Record;
139 using Ring = typename TypeParam::Ring;
140 Ring ring;
141 auto mmap = CreateRing(&ring, Ring::Traits::MinCount());
142 const uint32_t oldest_sequence_at_start = ring.GetOldestSequence();
143 const uint32_t next_sequence_at_start = ring.GetNextSequence();
144 {
145 uint32_t sequence = oldest_sequence_at_start;
146 Record record;
147 EXPECT_FALSE(ring.Get(&sequence, &record));
148 EXPECT_EQ(oldest_sequence_at_start, sequence);
149 EXPECT_EQ(Record(), record);
150 }
151 const Record original_record(0x1a);
152 ring.Put(original_record);
153 {
154 uint32_t sequence = next_sequence_at_start;
155 Record record;
156 EXPECT_TRUE(ring.Get(&sequence, &record));
157 EXPECT_EQ(next_sequence_at_start, sequence);
158 EXPECT_EQ(original_record, record);
159 }
160 {
161 uint32_t sequence = next_sequence_at_start + 1;
162 Record record;
163 EXPECT_FALSE(ring.Get(&sequence, &record));
164 EXPECT_EQ(next_sequence_at_start + 1, sequence);
165 EXPECT_EQ(Record(), record);
166 }
167 }
168
TYPED_TEST(BroadcastRingTest,FillOnce)169 TYPED_TEST(BroadcastRingTest, FillOnce) {
170 using Record = typename TypeParam::Record;
171 using Ring = typename TypeParam::Ring;
172 Ring ring;
173 auto mmap = CreateRing(&ring, Ring::Traits::MinCount());
174 const uint32_t next_sequence_at_start = ring.GetNextSequence();
175 for (uint32_t i = 0; i < ring.record_count(); ++i)
176 ring.Put(Record(FillChar(i)));
177 for (uint32_t i = 0; i < ring.record_count(); ++i) {
178 const uint32_t expected_sequence = next_sequence_at_start + i;
179 const Record expected_record(FillChar(i));
180 {
181 uint32_t sequence = ring.GetOldestSequence() + i;
182 Record record;
183 EXPECT_TRUE(ring.Get(&sequence, &record));
184 EXPECT_EQ(expected_sequence, sequence);
185 EXPECT_EQ(expected_record, record);
186 }
187 }
188 {
189 uint32_t sequence = ring.GetOldestSequence() + ring.record_count();
190 Record record;
191 EXPECT_FALSE(ring.Get(&sequence, &record));
192 }
193 }
194
TYPED_TEST(BroadcastRingTest,FillTwice)195 TYPED_TEST(BroadcastRingTest, FillTwice) {
196 using Record = typename TypeParam::Record;
197 using Ring = typename TypeParam::Ring;
198 Ring ring;
199 auto mmap = CreateRing(&ring, Ring::Traits::MinCount());
200 const uint32_t next_sequence_at_start = ring.GetNextSequence();
201 for (uint32_t i = 0; i < 2 * ring.record_count(); ++i) {
202 const Record newest_record(FillChar(i));
203 ring.Put(newest_record);
204
205 const uint32_t newest_sequence = next_sequence_at_start + i;
206 const uint32_t records_available = std::min(i + 1, ring.record_count());
207 const uint32_t oldest_sequence = newest_sequence - records_available + 1;
208 EXPECT_EQ(newest_sequence, ring.GetNewestSequence());
209 EXPECT_EQ(oldest_sequence, ring.GetOldestSequence());
210 EXPECT_EQ(newest_sequence + 1, ring.GetNextSequence());
211
212 for (uint32_t j = 0; j < records_available; ++j) {
213 const uint32_t sequence_jth_newest = newest_sequence - j;
214 const Record record_jth_newest(FillChar(i - j));
215
216 {
217 uint32_t sequence = sequence_jth_newest;
218 Record record;
219 EXPECT_TRUE(ring.Get(&sequence, &record));
220 EXPECT_EQ(sequence_jth_newest, sequence);
221 EXPECT_EQ(record_jth_newest, record);
222 }
223
224 {
225 uint32_t sequence = sequence_jth_newest;
226 Record record;
227 EXPECT_TRUE(ring.GetNewest(&sequence, &record));
228 EXPECT_EQ(newest_sequence, sequence);
229 EXPECT_EQ(newest_record, record);
230 }
231 }
232
233 const Record oldest_record(
234 FillChar(i + (oldest_sequence - newest_sequence)));
235 const uint32_t sequence_0th_overwritten = oldest_sequence - 1;
236 const uint32_t sequence_0th_future = newest_sequence + 1;
237 const uint32_t sequence_1st_future = newest_sequence + 2;
238
239 {
240 uint32_t sequence = sequence_0th_overwritten;
241 Record record;
242 EXPECT_TRUE(ring.Get(&sequence, &record));
243 EXPECT_EQ(oldest_sequence, sequence);
244 EXPECT_EQ(oldest_record, record);
245 }
246
247 {
248 uint32_t sequence = sequence_0th_overwritten;
249 Record record;
250 EXPECT_TRUE(ring.GetNewest(&sequence, &record));
251 EXPECT_EQ(newest_sequence, sequence);
252 EXPECT_EQ(newest_record, record);
253 }
254
255 {
256 uint32_t sequence = sequence_0th_future;
257 Record record;
258 EXPECT_FALSE(ring.Get(&sequence, &record));
259 EXPECT_EQ(sequence_0th_future, sequence);
260 EXPECT_EQ(Record(), record);
261 }
262
263 {
264 uint32_t sequence = sequence_0th_future;
265 Record record;
266 EXPECT_FALSE(ring.GetNewest(&sequence, &record));
267 EXPECT_EQ(sequence_0th_future, sequence);
268 EXPECT_EQ(Record(), record);
269 }
270
271 {
272 uint32_t sequence = sequence_1st_future;
273 Record record;
274 EXPECT_TRUE(ring.Get(&sequence, &record));
275 EXPECT_EQ(oldest_sequence, sequence);
276 EXPECT_EQ(oldest_record, record);
277 }
278
279 {
280 uint32_t sequence = sequence_1st_future;
281 Record record;
282 EXPECT_TRUE(ring.GetNewest(&sequence, &record));
283 EXPECT_EQ(newest_sequence, sequence);
284 EXPECT_EQ(newest_record, record);
285 }
286 }
287 }
288
TYPED_TEST(BroadcastRingTest,Import)289 TYPED_TEST(BroadcastRingTest, Import) {
290 using Record = typename TypeParam::Record;
291 using Ring = typename TypeParam::Ring;
292 Ring ring;
293 auto mmap = CreateRing(&ring, Ring::Traits::MinCount());
294
295 const uint32_t sequence_0 = ring.GetNextSequence();
296 const uint32_t sequence_1 = ring.GetNextSequence() + 1;
297 const Record record_0 = Record::Pattern(0x00);
298 const Record record_1 = Record::Pattern(0x80);
299 ring.Put(record_0);
300 ring.Put(record_1);
301
302 {
303 Ring imported_ring;
304 bool import_ok;
305 std::tie(imported_ring, import_ok) = Ring::Import(mmap.mmap(), mmap.size);
306 EXPECT_TRUE(import_ok);
307 EXPECT_EQ(ring.record_size(), imported_ring.record_size());
308 EXPECT_EQ(ring.record_count(), imported_ring.record_count());
309
310 if (ring.record_count() != 1) {
311 uint32_t sequence = sequence_0;
312 Record imported_record;
313 EXPECT_TRUE(imported_ring.Get(&sequence, &imported_record));
314 EXPECT_EQ(sequence_0, sequence);
315 EXPECT_EQ(record_0, imported_record);
316 }
317
318 {
319 uint32_t sequence = sequence_1;
320 Record imported_record;
321 EXPECT_TRUE(imported_ring.Get(&sequence, &imported_record));
322 EXPECT_EQ(sequence_1, sequence);
323 EXPECT_EQ(record_1, imported_record);
324 }
325 }
326 }
327
TEST(BroadcastRingTest,ShouldFailImportIfStaticSizeMismatch)328 TEST(BroadcastRingTest, ShouldFailImportIfStaticSizeMismatch) {
329 using OriginalRing = typename Static_16_16x16::Ring;
330 using RecordSizeMismatchRing = typename Static_8_8x16::Ring;
331 using RecordCountMismatchRing = typename Static_16_16x8::Ring;
332
333 OriginalRing original_ring;
334 auto mmap = CreateRing(&original_ring, OriginalRing::Traits::MinCount());
335
336 {
337 using ImportedRing = RecordSizeMismatchRing;
338 ImportedRing imported_ring;
339 bool import_ok;
340 std::tie(imported_ring, import_ok) =
341 ImportedRing::Import(mmap.mmap(), mmap.size);
342 EXPECT_FALSE(import_ok);
343 auto mmap_imported =
344 CreateRing(&imported_ring, ImportedRing::Traits::MinCount());
345 EXPECT_NE(original_ring.record_size(), imported_ring.record_size());
346 EXPECT_EQ(original_ring.record_count(), imported_ring.record_count());
347 }
348
349 {
350 using ImportedRing = RecordCountMismatchRing;
351 ImportedRing imported_ring;
352 bool import_ok;
353 std::tie(imported_ring, import_ok) =
354 ImportedRing::Import(mmap.mmap(), mmap.size);
355 EXPECT_FALSE(import_ok);
356 auto mmap_imported =
357 CreateRing(&imported_ring, ImportedRing::Traits::MinCount());
358 EXPECT_EQ(original_ring.record_size(), imported_ring.record_size());
359 EXPECT_NE(original_ring.record_count(), imported_ring.record_count());
360 }
361 }
362
TEST(BroadcastRingTest,ShouldFailImportIfDynamicSizeGrows)363 TEST(BroadcastRingTest, ShouldFailImportIfDynamicSizeGrows) {
364 using OriginalRing = typename Dynamic_8_NxM::Ring;
365 using RecordSizeGrowsRing = typename Dynamic_16_NxM::Ring;
366
367 OriginalRing original_ring;
368 auto mmap = CreateRing(&original_ring, OriginalRing::Traits::MinCount());
369
370 {
371 using ImportedRing = RecordSizeGrowsRing;
372 ImportedRing imported_ring;
373 bool import_ok;
374 std::tie(imported_ring, import_ok) =
375 ImportedRing::Import(mmap.mmap(), mmap.size);
376 EXPECT_FALSE(import_ok);
377 auto mmap_imported =
378 CreateRing(&imported_ring, ImportedRing::Traits::MinCount());
379 EXPECT_LT(original_ring.record_size(), imported_ring.record_size());
380 EXPECT_EQ(original_ring.record_count(), imported_ring.record_count());
381 }
382 }
383
TEST(BroadcastRingTest,ShouldFailImportIfCountTooSmall)384 TEST(BroadcastRingTest, ShouldFailImportIfCountTooSmall) {
385 using OriginalRing = typename Dynamic_16_NxM_1plus0::Ring;
386 using MinCountRing = typename Dynamic_16_NxM_1plus1::Ring;
387
388 OriginalRing original_ring;
389 auto mmap = CreateRing(&original_ring, OriginalRing::Traits::MinCount());
390
391 {
392 using ImportedRing = MinCountRing;
393 ImportedRing imported_ring;
394 bool import_ok;
395 std::tie(imported_ring, import_ok) =
396 ImportedRing::Import(mmap.mmap(), mmap.size);
397 EXPECT_FALSE(import_ok);
398 auto mmap_imported =
399 CreateRing(&imported_ring, ImportedRing::Traits::MinCount());
400 EXPECT_EQ(original_ring.record_size(), imported_ring.record_size());
401 EXPECT_LT(original_ring.record_count(), imported_ring.record_count());
402 }
403 }
404
TEST(BroadcastRingTest,ShouldFailImportIfMmapTooSmall)405 TEST(BroadcastRingTest, ShouldFailImportIfMmapTooSmall) {
406 using OriginalRing = typename Dynamic_16_NxM::Ring;
407
408 OriginalRing original_ring;
409 auto mmap = CreateRing(&original_ring, OriginalRing::Traits::MinCount());
410
411 {
412 using ImportedRing = OriginalRing;
413 ImportedRing imported_ring;
414 bool import_ok;
415 const size_t kMinSize =
416 ImportedRing::MemorySize(original_ring.record_count());
417 std::tie(imported_ring, import_ok) = ImportedRing::Import(mmap.mmap(), 0);
418 EXPECT_FALSE(import_ok);
419 std::tie(imported_ring, import_ok) =
420 ImportedRing::Import(mmap.mmap(), kMinSize - 1);
421 EXPECT_FALSE(import_ok);
422 std::tie(imported_ring, import_ok) =
423 ImportedRing::Import(mmap.mmap(), kMinSize);
424 EXPECT_TRUE(import_ok);
425 EXPECT_EQ(original_ring.record_size(), imported_ring.record_size());
426 EXPECT_EQ(original_ring.record_count(), imported_ring.record_count());
427 }
428 }
429
TEST(BroadcastRingTest,ShouldImportIfDynamicSizeShrinks)430 TEST(BroadcastRingTest, ShouldImportIfDynamicSizeShrinks) {
431 using OriginalRing = typename Dynamic_16_NxM::Ring;
432 using RecordSizeShrinksRing = typename Dynamic_8_NxM::Ring;
433
434 OriginalRing original_ring;
435 auto mmap = CreateRing(&original_ring, OriginalRing::Traits::MinCount());
436
437 using OriginalRecord = typename OriginalRing::Record;
438 const uint32_t original_sequence_0 = original_ring.GetNextSequence();
439 const uint32_t original_sequence_1 = original_ring.GetNextSequence() + 1;
440 const OriginalRecord original_record_0 = OriginalRecord::Pattern(0x00);
441 const OriginalRecord original_record_1 = OriginalRecord::Pattern(0x80);
442 original_ring.Put(original_record_0);
443 original_ring.Put(original_record_1);
444
445 {
446 using ImportedRing = RecordSizeShrinksRing;
447 using ImportedRecord = typename ImportedRing::Record;
448 ImportedRing imported_ring;
449 bool import_ok;
450 std::tie(imported_ring, import_ok) =
451 ImportedRing::Import(mmap.mmap(), mmap.size);
452 EXPECT_TRUE(import_ok);
453 EXPECT_EQ(original_ring.record_size(), imported_ring.record_size());
454 EXPECT_EQ(original_ring.record_count(), imported_ring.record_count());
455 EXPECT_GT(sizeof(OriginalRecord), sizeof(ImportedRecord));
456
457 {
458 uint32_t sequence = original_sequence_0;
459 ImportedRecord shrunk_record;
460 EXPECT_TRUE(imported_ring.Get(&sequence, &shrunk_record));
461 EXPECT_EQ(original_sequence_0, sequence);
462 EXPECT_EQ(original_record_0.Truncate<ImportedRecord>(), shrunk_record);
463 }
464
465 {
466 uint32_t sequence = original_sequence_1;
467 ImportedRecord shrunk_record;
468 EXPECT_TRUE(imported_ring.Get(&sequence, &shrunk_record));
469 EXPECT_EQ(original_sequence_1, sequence);
470 EXPECT_EQ(original_record_1.Truncate<ImportedRecord>(), shrunk_record);
471 }
472 }
473 }
474
TEST(BroadcastRingTest,ShouldImportIfCompatibleDynamicToStatic)475 TEST(BroadcastRingTest, ShouldImportIfCompatibleDynamicToStatic) {
476 using OriginalRing = typename Dynamic_16_NxM::Ring;
477 using ImportedRing = typename Static_16_16x16::Ring;
478 using OriginalRecord = typename OriginalRing::Record;
479 using ImportedRecord = typename ImportedRing::Record;
480 using StaticRing = ImportedRing;
481
482 OriginalRing original_ring;
483 auto mmap = CreateRing(&original_ring, StaticRing::Traits::MinCount());
484
485 const uint32_t original_sequence_0 = original_ring.GetNextSequence();
486 const uint32_t original_sequence_1 = original_ring.GetNextSequence() + 1;
487 const OriginalRecord original_record_0 = OriginalRecord::Pattern(0x00);
488 const OriginalRecord original_record_1 = OriginalRecord::Pattern(0x80);
489 original_ring.Put(original_record_0);
490 original_ring.Put(original_record_1);
491
492 {
493 ImportedRing imported_ring;
494 bool import_ok;
495 std::tie(imported_ring, import_ok) =
496 ImportedRing::Import(mmap.mmap(), mmap.size);
497 EXPECT_TRUE(import_ok);
498 EXPECT_EQ(original_ring.record_size(), imported_ring.record_size());
499 EXPECT_EQ(original_ring.record_count(), imported_ring.record_count());
500
501 {
502 uint32_t sequence = original_sequence_0;
503 ImportedRecord imported_record;
504 EXPECT_TRUE(imported_ring.Get(&sequence, &imported_record));
505 EXPECT_EQ(original_sequence_0, sequence);
506 EXPECT_EQ(original_record_0, imported_record);
507 }
508
509 {
510 uint32_t sequence = original_sequence_1;
511 ImportedRecord imported_record;
512 EXPECT_TRUE(imported_ring.Get(&sequence, &imported_record));
513 EXPECT_EQ(original_sequence_1, sequence);
514 EXPECT_EQ(original_record_1, imported_record);
515 }
516 }
517 }
518
TEST(BroadcastRingTest,ShouldImportIfCompatibleStaticToDynamic)519 TEST(BroadcastRingTest, ShouldImportIfCompatibleStaticToDynamic) {
520 using OriginalRing = typename Static_16_16x16::Ring;
521 using ImportedRing = typename Dynamic_16_NxM::Ring;
522 using OriginalRecord = typename OriginalRing::Record;
523 using ImportedRecord = typename ImportedRing::Record;
524 using StaticRing = OriginalRing;
525
526 OriginalRing original_ring;
527 auto mmap = CreateRing(&original_ring, StaticRing::Traits::MinCount());
528
529 const uint32_t original_sequence_0 = original_ring.GetNextSequence();
530 const uint32_t original_sequence_1 = original_ring.GetNextSequence() + 1;
531 const OriginalRecord original_record_0 = OriginalRecord::Pattern(0x00);
532 const OriginalRecord original_record_1 = OriginalRecord::Pattern(0x80);
533 original_ring.Put(original_record_0);
534 original_ring.Put(original_record_1);
535
536 {
537 ImportedRing imported_ring;
538 bool import_ok;
539 std::tie(imported_ring, import_ok) =
540 ImportedRing::Import(mmap.mmap(), mmap.size);
541 EXPECT_TRUE(import_ok);
542 EXPECT_EQ(original_ring.record_size(), imported_ring.record_size());
543 EXPECT_EQ(original_ring.record_count(), imported_ring.record_count());
544
545 {
546 uint32_t sequence = original_sequence_0;
547 ImportedRecord imported_record;
548 EXPECT_TRUE(imported_ring.Get(&sequence, &imported_record));
549 EXPECT_EQ(original_sequence_0, sequence);
550 EXPECT_EQ(original_record_0, imported_record);
551 }
552
553 {
554 uint32_t sequence = original_sequence_1;
555 ImportedRecord imported_record;
556 EXPECT_TRUE(imported_ring.Get(&sequence, &imported_record));
557 EXPECT_EQ(original_sequence_1, sequence);
558 EXPECT_EQ(original_record_1, imported_record);
559 }
560 }
561 }
562
TEST(BroadcastRingTest,ShouldImportIfReadonlyMmap)563 TEST(BroadcastRingTest, ShouldImportIfReadonlyMmap) {
564 using Ring = Dynamic_32_NxM::Ring;
565 using Record = Ring::Record;
566
567 uint32_t record_count = Ring::Traits::MinCount();
568 size_t ring_size = Ring::MemorySize(record_count);
569
570 size_t page_size = sysconf(_SC_PAGESIZE);
571 size_t mmap_size = (ring_size + (page_size - 1)) & ~(page_size - 1);
572 ASSERT_GE(mmap_size, ring_size);
573
574 void* mmap_base = mmap(nullptr, mmap_size, PROT_READ | PROT_WRITE,
575 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
576 ASSERT_NE(MAP_FAILED, mmap_base);
577
578 Ring ring = Ring::Create(mmap_base, mmap_size, record_count);
579 for (uint32_t i = 0; i < record_count; ++i) ring.Put(Record(FillChar(i)));
580
581 ASSERT_EQ(0, mprotect(mmap_base, mmap_size, PROT_READ));
582
583 {
584 Ring imported_ring;
585 bool import_ok;
586 std::tie(imported_ring, import_ok) = Ring::Import(mmap_base, mmap_size);
587 EXPECT_TRUE(import_ok);
588 EXPECT_EQ(ring.record_size(), imported_ring.record_size());
589 EXPECT_EQ(ring.record_count(), imported_ring.record_count());
590
591 uint32_t oldest_sequence = imported_ring.GetOldestSequence();
592 for (uint32_t i = 0; i < record_count; ++i) {
593 uint32_t sequence = oldest_sequence + i;
594 Record record;
595 EXPECT_TRUE(imported_ring.Get(&sequence, &record));
596 EXPECT_EQ(Record(FillChar(i)), record);
597 }
598 }
599
600 ASSERT_EQ(0, munmap(mmap_base, mmap_size));
601 }
602
TEST(BroadcastRingTest,ShouldDieIfPutReadonlyMmap)603 TEST(BroadcastRingTest, ShouldDieIfPutReadonlyMmap) {
604 using Ring = Dynamic_32_NxM::Ring;
605 using Record = Ring::Record;
606
607 uint32_t record_count = Ring::Traits::MinCount();
608 size_t ring_size = Ring::MemorySize(record_count);
609
610 size_t page_size = sysconf(_SC_PAGESIZE);
611 size_t mmap_size = (ring_size + (page_size - 1)) & ~(page_size - 1);
612 ASSERT_GE(mmap_size, ring_size);
613
614 void* mmap_base = mmap(nullptr, mmap_size, PROT_READ | PROT_WRITE,
615 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
616 ASSERT_NE(MAP_FAILED, mmap_base);
617
618 Ring ring = Ring::Create(mmap_base, mmap_size, record_count);
619 for (uint32_t i = 0; i < record_count; ++i) ring.Put(Record(FillChar(i)));
620
621 ASSERT_EQ(0, mprotect(mmap_base, mmap_size, PROT_READ));
622
623 EXPECT_DEATH_IF_SUPPORTED({ ring.Put(Record(7)); }, "");
624
625 ASSERT_EQ(0, munmap(mmap_base, mmap_size));
626 }
627
TEST(BroadcastRingTest,ShouldDieIfCreationMmapTooSmall)628 TEST(BroadcastRingTest, ShouldDieIfCreationMmapTooSmall) {
629 using Ring = Dynamic_32_NxM::Ring;
630 using Record = Ring::Record;
631
632 uint32_t record_count = Ring::Traits::MinCount();
633 size_t ring_size = Ring::MemorySize(record_count);
634 FakeMmap mmap(ring_size);
635
636 EXPECT_DEATH_IF_SUPPORTED({
637 Ring ring = Ring::Create(mmap.mmap(), ring_size - 1, record_count);
638 }, "");
639
640 Ring ring = Ring::Create(mmap.mmap(), ring_size, record_count);
641
642 ring.Put(Record(3));
643
644 {
645 uint32_t sequence = ring.GetNewestSequence();
646 Record record;
647 EXPECT_TRUE(ring.Get(&sequence, &record));
648 EXPECT_EQ(Record(3), record);
649 }
650 }
651
TEST(BroadcastRingTest,ShouldDieIfCreationMmapMisaligned)652 TEST(BroadcastRingTest, ShouldDieIfCreationMmapMisaligned) {
653 using Ring = Static_8_8x1::Ring;
654 using Record = Ring::Record;
655
656 constexpr int kAlign = Ring::mmap_alignment();
657 constexpr int kMisalign = kAlign / 2;
658 size_t ring_size = Ring::MemorySize();
659 std::unique_ptr<char[]> buf(new char[ring_size + kMisalign]);
660
661 EXPECT_DEATH_IF_SUPPORTED(
662 { Ring ring = Ring::Create(buf.get() + kMisalign, ring_size); }, "");
663
664 Ring ring = Ring::Create(buf.get(), ring_size);
665
666 ring.Put(Record(3));
667
668 {
669 uint32_t sequence = ring.GetNewestSequence();
670 Record record;
671 EXPECT_TRUE(ring.Get(&sequence, &record));
672 EXPECT_EQ(Record(3), record);
673 }
674 }
675
676 template <typename Ring>
CopyTask(std::atomic<bool> * quit,void * in_base,size_t in_size,void * out_base,size_t out_size)677 std::unique_ptr<std::thread> CopyTask(std::atomic<bool>* quit, void* in_base,
678 size_t in_size, void* out_base,
679 size_t out_size) {
680 return std::unique_ptr<std::thread>(
681 new std::thread([quit, in_base, in_size, out_base, out_size]() {
682 using Record = typename Ring::Record;
683
684 bool import_ok;
685 Ring in_ring;
686 Ring out_ring;
687 std::tie(in_ring, import_ok) = Ring::Import(in_base, in_size);
688 ASSERT_TRUE(import_ok);
689 std::tie(out_ring, import_ok) = Ring::Import(out_base, out_size);
690 ASSERT_TRUE(import_ok);
691
692 uint32_t sequence = in_ring.GetOldestSequence();
693 while (!std::atomic_load_explicit(quit, std::memory_order_relaxed)) {
694 Record record;
695 if (in_ring.Get(&sequence, &record)) {
696 out_ring.Put(record);
697 sequence++;
698 }
699 }
700 }));
701 }
702
TEST(BroadcastRingTest,ThreadedCopySingle)703 TEST(BroadcastRingTest, ThreadedCopySingle) {
704 using Ring = Dynamic_32_NxM::Ring;
705 using Record = Ring::Record;
706 Ring in_ring;
707 auto in_mmap = CreateRing(&in_ring, Ring::Traits::MinCount());
708
709 Ring out_ring;
710 auto out_mmap = CreateRing(&out_ring, Ring::Traits::MinCount());
711
712 std::atomic<bool> quit(false);
713 std::unique_ptr<std::thread> copy_task = CopyTask<Ring>(
714 &quit, out_mmap.mmap(), out_mmap.size, in_mmap.mmap(), in_mmap.size);
715
716 const Record out_record(0x1c);
717 out_ring.Put(out_record);
718
719 uint32_t in_sequence = in_ring.GetOldestSequence();
720 Record in_record;
721 while (!in_ring.Get(&in_sequence, &in_record)) {
722 // Do nothing.
723 }
724
725 EXPECT_EQ(out_record, in_record);
726 std::atomic_store_explicit(&quit, true, std::memory_order_relaxed);
727 copy_task->join();
728 }
729
TEST(BroadcastRingTest,ThreadedCopyLossless)730 TEST(BroadcastRingTest, ThreadedCopyLossless) {
731 using Ring = Dynamic_32_NxM::Ring;
732 using Record = Ring::Record;
733 Ring in_ring;
734 auto in_mmap = CreateRing(&in_ring, Ring::Traits::MinCount());
735
736 Ring out_ring;
737 auto out_mmap = CreateRing(&out_ring, Ring::Traits::MinCount());
738
739 std::atomic<bool> quit(false);
740 std::unique_ptr<std::thread> copy_task = CopyTask<Ring>(
741 &quit, out_mmap.mmap(), out_mmap.size, in_mmap.mmap(), in_mmap.size);
742
743 constexpr uint32_t kRecordsToProcess = 10000;
744 uint32_t out_records = 0;
745 uint32_t in_records = 0;
746 uint32_t in_sequence = in_ring.GetNextSequence();
747 while (out_records < kRecordsToProcess || in_records < kRecordsToProcess) {
748 if (out_records < kRecordsToProcess &&
749 out_records - in_records < out_ring.record_count()) {
750 const Record out_record(FillChar(out_records));
751 out_ring.Put(out_record);
752 out_records++;
753 }
754
755 Record in_record;
756 while (in_ring.Get(&in_sequence, &in_record)) {
757 EXPECT_EQ(Record(FillChar(in_records)), in_record);
758 in_records++;
759 in_sequence++;
760 }
761 }
762
763 EXPECT_EQ(kRecordsToProcess, out_records);
764 EXPECT_EQ(kRecordsToProcess, in_records);
765
766 std::atomic_store_explicit(&quit, true, std::memory_order_relaxed);
767 copy_task->join();
768 }
769
TEST(BroadcastRingTest,ThreadedCopyLossy)770 TEST(BroadcastRingTest, ThreadedCopyLossy) {
771 using Ring = Dynamic_32_NxM::Ring;
772 using Record = Ring::Record;
773 Ring in_ring;
774 auto in_mmap = CreateRing(&in_ring, Ring::Traits::MinCount());
775
776 Ring out_ring;
777 auto out_mmap = CreateRing(&out_ring, Ring::Traits::MinCount());
778
779 std::atomic<bool> quit(false);
780 std::unique_ptr<std::thread> copy_task = CopyTask<Ring>(
781 &quit, out_mmap.mmap(), out_mmap.size, in_mmap.mmap(), in_mmap.size);
782
783 constexpr uint32_t kRecordsToProcess = 100000;
784 uint32_t out_records = 0;
785 uint32_t in_records = 0;
786 uint32_t in_sequence = in_ring.GetNextSequence();
787 while (out_records < kRecordsToProcess) {
788 const Record out_record(FillChar(out_records));
789 out_ring.Put(out_record);
790 out_records++;
791
792 Record in_record;
793 if (in_ring.GetNewest(&in_sequence, &in_record)) {
794 EXPECT_EQ(Record(in_record.v[0]), in_record);
795 in_records++;
796 in_sequence++;
797 }
798 }
799
800 EXPECT_EQ(kRecordsToProcess, out_records);
801 EXPECT_GE(kRecordsToProcess, in_records);
802
803 std::atomic_store_explicit(&quit, true, std::memory_order_relaxed);
804 copy_task->join();
805 }
806
807 template <typename Ring>
CheckFillTask(std::atomic<bool> * quit,void * in_base,size_t in_size)808 std::unique_ptr<std::thread> CheckFillTask(std::atomic<bool>* quit,
809 void* in_base, size_t in_size) {
810 return std::unique_ptr<std::thread>(
811 new std::thread([quit, in_base, in_size]() {
812 using Record = typename Ring::Record;
813
814 bool import_ok;
815 Ring in_ring;
816 std::tie(in_ring, import_ok) = Ring::Import(in_base, in_size);
817 ASSERT_TRUE(import_ok);
818
819 uint32_t sequence = in_ring.GetOldestSequence();
820 while (!std::atomic_load_explicit(quit, std::memory_order_relaxed)) {
821 Record record;
822 if (in_ring.Get(&sequence, &record)) {
823 ASSERT_EQ(Record(record.v[0]), record);
824 sequence++;
825 }
826 }
827 }));
828 }
829
830 template <typename Ring>
ThreadedOverwriteTorture()831 void ThreadedOverwriteTorture() {
832 using Record = typename Ring::Record;
833
834 // Maximize overwrites by having few records.
835 const int kMinRecordCount = 1;
836 const int kMaxRecordCount = 4;
837
838 for (int count = kMinRecordCount; count <= kMaxRecordCount; count *= 2) {
839 Ring out_ring;
840 auto out_mmap = CreateRing(&out_ring, count);
841
842 std::atomic<bool> quit(false);
843 std::unique_ptr<std::thread> check_task =
844 CheckFillTask<Ring>(&quit, out_mmap.mmap(), out_mmap.size);
845
846 constexpr int kIterations = 10000;
847 for (int i = 0; i < kIterations; ++i) {
848 const Record record(FillChar(i));
849 out_ring.Put(record);
850 }
851
852 std::atomic_store_explicit(&quit, true, std::memory_order_relaxed);
853 check_task->join();
854 }
855 }
856
TEST(BroadcastRingTest,ThreadedOverwriteTortureSmall)857 TEST(BroadcastRingTest, ThreadedOverwriteTortureSmall) {
858 ThreadedOverwriteTorture<Dynamic_16_NxM_1plus0::Ring>();
859 }
860
TEST(BroadcastRingTest,ThreadedOverwriteTortureLarge)861 TEST(BroadcastRingTest, ThreadedOverwriteTortureLarge) {
862 ThreadedOverwriteTorture<Dynamic_256_NxM_1plus0::Ring>();
863 }
864
865 } // namespace dvr
866 } // namespace android
867