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