Home | History | Annotate | Download | only in libbroadcastring
      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 {
     21   Sized() { Clear(); }
     22   explicit Sized(char c) { Fill(c); }
     23   char v[sizeof(Aligned<N>)];
     24   void Clear() { memset(v, 0, sizeof(v)); }
     25   void Fill(char c) { memset(v, c, sizeof(v)); }
     26   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   }
     33   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>
     38   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 
     46 char FillChar(int val) { return static_cast<char>(val); }
     47 
     48 struct FakeMmap {
     49   explicit FakeMmap(size_t size) : size(size), data(new char[size]) {}
     50   size_t size;
     51   std::unique_ptr<char[]> data;
     52   void* mmap() { return static_cast<void*>(data.get()); }
     53 };
     54 
     55 template <typename Ring>
     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>;
     79   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>;
     87   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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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>
    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 
    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 
    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 
    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>
    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>
    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 
    857 TEST(BroadcastRingTest, ThreadedOverwriteTortureSmall) {
    858   ThreadedOverwriteTorture<Dynamic_16_NxM_1plus0::Ring>();
    859 }
    860 
    861 TEST(BroadcastRingTest, ThreadedOverwriteTortureLarge) {
    862   ThreadedOverwriteTorture<Dynamic_256_NxM_1plus0::Ring>();
    863 }
    864 
    865 } // namespace dvr
    866 } // namespace android
    867