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
10namespace android {
11namespace dvr {
12namespace {
13
14template <uint32_t N>
15struct alignas(8) Aligned {
16  char v[N];
17};
18
19template <uint32_t N>
20struct 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
46char FillChar(int val) { return static_cast<char>(val); }
47
48struct 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
55template <typename Ring>
56FakeMmap 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
62template <typename RecordType, bool StaticSize = false,
63          uint32_t StaticCount = 0, uint32_t MaxReserved = 1,
64          uint32_t MinAvailable = 0>
65struct 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
74template <typename Record, bool StaticSize = false, uint32_t MaxReserved = 1,
75          uint32_t MinAvailable = 7>
76struct 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
82template <typename Record, uint32_t StaticCount = 1, bool StaticSize = true,
83          uint32_t MaxReserved = 1, uint32_t MinAvailable = 0>
84struct TraitsStatic
85    : public Traits<Record, true, StaticCount, MaxReserved, MinAvailable> {
86  using Ring = BroadcastRing<Record, TraitsStatic>;
87  static uint32_t MinCount() { return StaticCount; }
88};
89
90using Dynamic_8_NxM = TraitsDynamic<Sized<8>>;
91using Dynamic_16_NxM = TraitsDynamic<Sized<16>>;
92using Dynamic_32_NxM = TraitsDynamic<Sized<32>>;
93using Dynamic_32_32xM = TraitsDynamic<Sized<32>, true>;
94using Dynamic_16_NxM_1plus0 = TraitsDynamic<Sized<16>, false, 1, 0>;
95using Dynamic_16_NxM_1plus1 = TraitsDynamic<Sized<16>, false, 1, 1>;
96using Dynamic_16_NxM_5plus11 = TraitsDynamic<Sized<16>, false, 5, 11>;
97using Dynamic_256_NxM_1plus0 = TraitsDynamic<Sized<256>, false, 1, 0>;
98
99using Static_8_8x1 = TraitsStatic<Sized<8>, 1>;
100using Static_8_8x16 = TraitsStatic<Sized<8>, 16>;
101using Static_16_16x8 = TraitsStatic<Sized<16>, 8>;
102using Static_16_16x16 = TraitsStatic<Sized<16>, 16>;
103using Static_16_16x32 = TraitsStatic<Sized<16>, 32>;
104using Static_32_Nx8 = TraitsStatic<Sized<32>, 8, false>;
105
106using 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
123template <typename T>
124class BroadcastRingTest : public ::testing::Test {};
125
126TYPED_TEST_CASE(BroadcastRingTest, TraitsList);
127
128TYPED_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
137TYPED_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
169TYPED_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
195TYPED_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
289TYPED_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
328TEST(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
363TEST(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
384TEST(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
405TEST(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
430TEST(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
475TEST(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
519TEST(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
563TEST(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
603TEST(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
628TEST(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
652TEST(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
676template <typename Ring>
677std::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
703TEST(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
730TEST(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
770TEST(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
807template <typename Ring>
808std::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
830template <typename Ring>
831void 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
857TEST(BroadcastRingTest, ThreadedOverwriteTortureSmall) {
858  ThreadedOverwriteTorture<Dynamic_16_NxM_1plus0::Ring>();
859}
860
861TEST(BroadcastRingTest, ThreadedOverwriteTortureLarge) {
862  ThreadedOverwriteTorture<Dynamic_256_NxM_1plus0::Ring>();
863}
864
865} // namespace dvr
866} // namespace android
867