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