1 #ifndef ANDROID_DVR_BROADCAST_RING_H_ 2 #define ANDROID_DVR_BROADCAST_RING_H_ 3 4 #include <inttypes.h> 5 #include <stddef.h> 6 #include <stdio.h> 7 #include <atomic> 8 #include <limits> 9 #include <tuple> 10 #include <type_traits> 11 #include <utility> 12 13 #include "android-base/logging.h" 14 15 #if ATOMIC_LONG_LOCK_FREE != 2 || ATOMIC_INT_LOCK_FREE != 2 16 #error "This file requires lock free atomic uint32_t and long" 17 #endif 18 19 namespace android { 20 namespace dvr { 21 22 struct DefaultRingTraits { 23 // Set this to false to allow compatibly expanding the record size. 24 static constexpr bool kUseStaticRecordSize = false; 25 26 // Set this to a nonzero value to fix the number of records in the ring. 27 static constexpr uint32_t kStaticRecordCount = 0; 28 29 // Set this to the max number of records that can be written simultaneously. 30 static constexpr uint32_t kMaxReservedRecords = 1; 31 32 // Set this to the min number of records that must be readable. 33 static constexpr uint32_t kMinAvailableRecords = 1; 34 }; 35 36 // Nonblocking ring suitable for concurrent single-writer, multi-reader access. 37 // 38 // Readers never block the writer and thus this is a nondeterministically lossy 39 // transport in the absence of external synchronization. Don't use this as a 40 // transport when deterministic behavior is required. 41 // 42 // Readers may have a read-only mapping; each reader's state is a single local 43 // sequence number. 44 // 45 // The implementation takes care to avoid data races on record access. 46 // Inconsistent data can only be returned if at least 2^32 records are written 47 // during the read-side critical section. 48 // 49 // In addition, both readers and the writer are careful to avoid accesses 50 // outside the bounds of the mmap area passed in during initialization even if 51 // there is a misbehaving or malicious task with write access to the mmap area. 52 // 53 // When dynamic record size is enabled, readers use the record size in the ring 54 // header when indexing the ring, so that it is possible to extend the record 55 // type without breaking the read-side ABI. 56 // 57 // Avoid calling Put() in a tight loop; there should be significantly more time 58 // between successive puts than it takes to read one record from memory to 59 // ensure Get() completes quickly. This requirement should not be difficult to 60 // achieve for most practical uses; 4kB puts at 10,000Hz is well below the 61 // scaling limit on current mobile chips. 62 // 63 // Example Writer Usage: 64 // 65 // using Record = MyRecordType; 66 // using Ring = BroadcastRing<Record>; 67 // 68 // uint32_t record_count = kMyDesiredCount; 69 // uint32_t ring_size = Ring::MemorySize(record_count); 70 // 71 // size_t page_size = sysconf(_SC_PAGESIZE); 72 // uint32_t mmap_size = (ring_size + (page_size - 1)) & ~(page_size - 1); 73 // 74 // // Allocate & map via your preferred mechanism, e.g. 75 // int fd = open("/dev/shm/ring_test", O_CREAT|O_RDWR|O_CLOEXEC, 0600); 76 // CHECK(fd >= 0); 77 // CHECK(!ftruncate(fd, ring_size)); 78 // void *mmap_base = mmap(nullptr, mmap_size, PROT_READ|PROT_WRITE, 79 // MAP_SHARED, fd, 0); 80 // CHECK(mmap_base != MAP_FAILED); 81 // close(fd); 82 // 83 // Ring ring = Ring::Create(mmap_base, mmap_size, record_count); 84 // 85 // while (!done) 86 // ring.Put(BuildNextRecordBlocking()); 87 // 88 // CHECK(!munmap(mmap_base, mmap_size)); 89 // 90 // Example Reader Usage: 91 // 92 // using Record = MyRecordType; 93 // using Ring = BroadcastRing<Record>; 94 // 95 // // Map via your preferred mechanism, e.g. 96 // int fd = open("/dev/shm/ring_test", O_RDONLY|O_CLOEXEC); 97 // CHECK(fd >= 0); 98 // struct stat st; 99 // CHECK(!fstat(fd, &st)); 100 // size_t mmap_size = st.st_size; 101 // void *mmap_base = mmap(nullptr, mmap_size, PROT_READ, 102 // MAP_SHARED, fd, 0); 103 // CHECK(mmap_base != MAP_FAILED); 104 // close(fd); 105 // 106 // Ring ring; 107 // bool import_ok; 108 // std::tie(ring, import_ok) = Ring::Import(mmap_base, mmap_size); 109 // CHECK(import_ok); 110 // 111 // uint32_t sequence; 112 // 113 // // Choose starting point (using "0" is unpredictable but not dangerous) 114 // sequence = ring.GetOldestSequence(); // The oldest available 115 // sequence = ring.GetNewestSequence(); // The newest available 116 // sequence = ring.GetNextSequence(); // The next one produced 117 // 118 // while (!done) { 119 // Record record; 120 // 121 // if (you_want_to_process_all_available_records) { 122 // while (ring.Get(&sequence, &record)) { 123 // ProcessRecord(sequence, record); 124 // sequence++; 125 // } 126 // } else if (you_want_to_skip_to_the_newest_record) { 127 // if (ring.GetNewest(&sequence, &record)) { 128 // ProcessRecord(sequence, record); 129 // sequence++; 130 // } 131 // } 132 // 133 // DoSomethingExpensiveOrBlocking(); 134 // } 135 // 136 // CHECK(!munmap(mmap_base, mmap_size)); 137 // 138 template <typename RecordType, typename BaseTraits = DefaultRingTraits> 139 class BroadcastRing { 140 public: 141 using Record = RecordType; 142 struct Traits : public BaseTraits { 143 // Must have enough space for writers, plus enough space for readers. 144 static constexpr int kMinRecordCount = 145 BaseTraits::kMaxReservedRecords + BaseTraits::kMinAvailableRecords; 146 147 // Count of zero means dynamic, non-zero means static. 148 static constexpr bool kUseStaticRecordCount = 149 (BaseTraits::kStaticRecordCount != 0); 150 151 // If both record size and count are static then the overall size is too. 152 static constexpr bool kIsStaticSize = 153 BaseTraits::kUseStaticRecordSize && kUseStaticRecordCount; 154 }; 155 IsPowerOfTwo(uint32_t size)156 static constexpr bool IsPowerOfTwo(uint32_t size) { 157 return (size & (size - 1)) == 0; 158 } 159 160 // Sanity check the options provided in Traits. 161 static_assert(Traits::kMinRecordCount >= 1, "Min record count too small"); 162 static_assert(!Traits::kUseStaticRecordCount || 163 Traits::kStaticRecordCount >= Traits::kMinRecordCount, 164 "Static record count is too small"); 165 static_assert(!Traits::kStaticRecordCount || 166 IsPowerOfTwo(Traits::kStaticRecordCount), 167 "Static record count is not a power of two"); 168 static_assert(std::is_standard_layout<Record>::value, 169 "Record type must be standard layout"); 170 BroadcastRing()171 BroadcastRing() {} 172 173 // Creates a new ring at |mmap| with |record_count| records. 174 // 175 // There must be at least |MemorySize(record_count)| bytes of space already 176 // allocated at |mmap|. The ring does not take ownership. Create(void * mmap,size_t mmap_size,uint32_t record_count)177 static BroadcastRing Create(void* mmap, size_t mmap_size, 178 uint32_t record_count) { 179 BroadcastRing ring(mmap); 180 CHECK(ring.ValidateGeometry(mmap_size, sizeof(Record), record_count)); 181 ring.InitializeHeader(sizeof(Record), record_count); 182 return ring; 183 } 184 185 // Creates a new ring at |mmap|. 186 // 187 // There must be at least |MemorySize()| bytes of space already allocated at 188 // |mmap|. The ring does not take ownership. Create(void * mmap,size_t mmap_size)189 static BroadcastRing Create(void* mmap, size_t mmap_size) { 190 return Create(mmap, mmap_size, 191 Traits::kUseStaticRecordCount 192 ? Traits::kStaticRecordCount 193 : BroadcastRing::GetRecordCount(mmap_size)); 194 } 195 196 // Imports an existing ring at |mmap|. 197 // 198 // Import may fail if the ring parameters in the mmap header are not sensible. 199 // In this case the returned boolean is false; make sure to check this value. Import(void * mmap,size_t mmap_size)200 static std::tuple<BroadcastRing, bool> Import(void* mmap, size_t mmap_size) { 201 BroadcastRing ring(mmap); 202 uint32_t record_size = 0; 203 uint32_t record_count = 0; 204 if (mmap_size >= sizeof(Header)) { 205 record_size = std::atomic_load_explicit(&ring.header_mmap()->record_size, 206 std::memory_order_relaxed); 207 record_count = std::atomic_load_explicit( 208 &ring.header_mmap()->record_count, std::memory_order_relaxed); 209 } 210 bool ok = ring.ValidateGeometry(mmap_size, record_size, record_count); 211 return std::make_tuple(ring, ok); 212 } 213 ~BroadcastRing()214 ~BroadcastRing() {} 215 216 // Calculates the space necessary for a ring of size |record_count|. 217 // 218 // Use this function for dynamically sized rings. MemorySize(uint32_t record_count)219 static constexpr size_t MemorySize(uint32_t record_count) { 220 return sizeof(Header) + sizeof(Record) * record_count; 221 } 222 223 // Calculates the space necessary for a statically sized ring. 224 // 225 // Use this function for statically sized rings. MemorySize()226 static constexpr size_t MemorySize() { 227 static_assert( 228 Traits::kUseStaticRecordCount, 229 "Wrong MemorySize() function called for dynamic record count"); 230 return MemorySize(Traits::kStaticRecordCount); 231 } 232 NextPowerOf2(uint32_t n)233 static uint32_t NextPowerOf2(uint32_t n) { 234 if (n == 0) 235 return 0; 236 n -= 1; 237 n |= n >> 16; 238 n |= n >> 8; 239 n |= n >> 4; 240 n |= n >> 2; 241 n |= n >> 1; 242 return n + 1; 243 } 244 245 // Gets the biggest power of 2 record count that can fit into this mmap. 246 // 247 // The header size has been taken into account. GetRecordCount(size_t mmap_size)248 static uint32_t GetRecordCount(size_t mmap_size) { 249 if (mmap_size <= sizeof(Header)) { 250 return 0; 251 } 252 uint32_t count = 253 static_cast<uint32_t>((mmap_size - sizeof(Header)) / sizeof(Record)); 254 return IsPowerOfTwo(count) ? count : (NextPowerOf2(count) / 2); 255 } 256 257 // Writes a record to the ring. 258 // 259 // The oldest record is overwritten unless the ring is not already full. Put(const Record & record)260 void Put(const Record& record) { 261 const int kRecordCount = 1; 262 Reserve(kRecordCount); 263 Geometry geometry = GetGeometry(); 264 PutRecordInternal(&record, record_mmap_writer(geometry.tail_index)); 265 Publish(kRecordCount); 266 } 267 268 // Gets sequence number of the oldest currently available record. GetOldestSequence()269 uint32_t GetOldestSequence() const { 270 return std::atomic_load_explicit(&header_mmap()->head, 271 std::memory_order_relaxed); 272 } 273 274 // Gets sequence number of the first future record. 275 // 276 // If the returned value is passed to Get() and there is no concurrent Put(), 277 // Get() will return false. GetNextSequence()278 uint32_t GetNextSequence() const { 279 return std::atomic_load_explicit(&header_mmap()->tail, 280 std::memory_order_relaxed); 281 } 282 283 // Gets sequence number of the newest currently available record. GetNewestSequence()284 uint32_t GetNewestSequence() const { return GetNextSequence() - 1; } 285 286 // Copies the oldest available record with sequence at least |*sequence| to 287 // |record|. 288 // 289 // Returns false if there is no recent enough record available. 290 // 291 // Updates |*sequence| with the sequence number of the record returned. To get 292 // the following record, increment this number by one. 293 // 294 // This function synchronizes with two other operations: 295 // 296 // (1) Load-Acquire of |tail| 297 // 298 // Together with the store-release in Publish(), this load-acquire 299 // ensures each store to a record in PutRecordInternal() happens-before 300 // any corresponding load in GetRecordInternal(). 301 // 302 // i.e. the stores for the records with sequence numbers < |tail| have 303 // completed from our perspective 304 // 305 // (2) Acquire Fence between record access & final load of |head| 306 // 307 // Together with the release fence in Reserve(), this ensures that if 308 // GetRecordInternal() loads a value stored in some execution of 309 // PutRecordInternal(), then the store of |head| in the Reserve() that 310 // preceeded it happens-before our final load of |head|. 311 // 312 // i.e. if we read a record with sequence number >= |final_head| then 313 // no later store to that record has completed from our perspective Get(uint32_t * sequence,Record * record)314 bool Get(uint32_t* sequence /*inout*/, Record* record /*out*/) const { 315 for (;;) { 316 uint32_t tail = std::atomic_load_explicit(&header_mmap()->tail, 317 std::memory_order_acquire); 318 uint32_t head = std::atomic_load_explicit(&header_mmap()->head, 319 std::memory_order_relaxed); 320 321 if (tail - head > record_count()) 322 continue; // Concurrent modification; re-try. 323 324 if (*sequence - head > tail - head) 325 *sequence = head; // Out of window, skip forward to first available. 326 327 if (*sequence == tail) return false; // No new records available. 328 329 Geometry geometry = 330 CalculateGeometry(record_count(), record_size(), *sequence, tail); 331 332 // Compute address explicitly in case record_size > sizeof(Record). 333 RecordStorage* record_storage = record_mmap_reader(geometry.head_index); 334 335 GetRecordInternal(record_storage, record); 336 337 // NB: It is not sufficient to change this to a load-acquire of |head|. 338 std::atomic_thread_fence(std::memory_order_acquire); 339 340 uint32_t final_head = std::atomic_load_explicit( 341 &header_mmap()->head, std::memory_order_relaxed); 342 343 if (final_head - head > *sequence - head) 344 continue; // Concurrent modification; re-try. 345 346 // Note: Combining the above 4 comparisons gives: 347 // 0 <= final_head - head <= sequence - head < tail - head <= record_count 348 // 349 // We can also write this as: 350 // head <=* final_head <=* sequence <* tail <=* head + record_count 351 // 352 // where <* orders by difference from head: x <* y if x - head < y - head. 353 // This agrees with the order of sequence updates during "put" operations. 354 return true; 355 } 356 } 357 358 // Copies the newest available record with sequence at least |*sequence| to 359 // |record|. 360 // 361 // Returns false if there is no recent enough record available. 362 // 363 // Updates |*sequence| with the sequence number of the record returned. To get 364 // the following record, increment this number by one. GetNewest(uint32_t * sequence,Record * record)365 bool GetNewest(uint32_t* sequence, Record* record) const { 366 uint32_t newest_sequence = GetNewestSequence(); 367 if (*sequence == newest_sequence + 1) return false; 368 *sequence = newest_sequence; 369 return Get(sequence, record); 370 } 371 372 // Returns true if this instance has been created or imported. is_valid()373 bool is_valid() const { return !!data_.mmap; } 374 record_count()375 uint32_t record_count() const { return record_count_internal(); } record_size()376 uint32_t record_size() const { return record_size_internal(); } mmap_alignment()377 static constexpr uint32_t mmap_alignment() { return alignof(Mmap); } 378 379 private: 380 struct Header { 381 // Record size for reading out of the ring. Writers always write the full 382 // length; readers may need to read a prefix of each record. 383 std::atomic<uint32_t> record_size; 384 385 // Number of records in the ring. 386 std::atomic<uint32_t> record_count; 387 388 // Readable region is [head % record_count, tail % record_count). 389 // 390 // The region in [tail % record_count, head % record_count) was either never 391 // populated or is being updated. 392 // 393 // These are sequences numbers, not indexes - indexes should be computed 394 // with a modulus. 395 // 396 // To ensure consistency: 397 // 398 // (1) Writes advance |head| past any updated records before writing to 399 // them, and advance |tail| after they are written. 400 // (2) Readers check |tail| before reading data and |head| after, 401 // making sure to discard any data that was written to concurrently. 402 std::atomic<uint32_t> head; 403 std::atomic<uint32_t> tail; 404 }; 405 406 // Store using the standard word size. 407 using StorageType = long; // NOLINT 408 409 // Always require 8 byte alignment so that the same record sizes are legal on 410 // 32 and 64 bit builds. 411 static constexpr size_t kRecordAlignment = 8; 412 static_assert(kRecordAlignment % sizeof(StorageType) == 0, 413 "Bad record alignment"); 414 415 struct RecordStorage { 416 // This is accessed with relaxed atomics to prevent data races on the 417 // contained data, which would be undefined behavior. 418 std::atomic<StorageType> data[sizeof(Record) / sizeof(StorageType)]; 419 }; 420 421 static_assert(sizeof(StorageType) * 422 std::extent<decltype(RecordStorage::data)>() == 423 sizeof(Record), 424 "Record length must be a multiple of sizeof(StorageType)"); 425 426 struct Geometry { 427 // Static geometry. 428 uint32_t record_count; 429 uint32_t record_size; 430 431 // Copy of atomic sequence counts. 432 uint32_t head; 433 uint32_t tail; 434 435 // First index of readable region. 436 uint32_t head_index; 437 438 // First index of writable region. 439 uint32_t tail_index; 440 441 // Number of records in readable region. 442 uint32_t count; 443 444 // Number of records in writable region. 445 uint32_t space; 446 }; 447 448 // Mmap area layout. 449 // 450 // Readers should not index directly into |records| as this is not valid when 451 // dynamic record sizes are used; use record_mmap_reader() instead. 452 struct Mmap { 453 Header header; 454 RecordStorage records[]; 455 }; 456 457 static_assert(std::is_standard_layout<Mmap>::value, 458 "Mmap must be standard layout"); 459 static_assert(sizeof(std::atomic<uint32_t>) == sizeof(uint32_t), 460 "Lockless atomics contain extra state"); 461 static_assert(sizeof(std::atomic<StorageType>) == sizeof(StorageType), 462 "Lockless atomics contain extra state"); 463 BroadcastRing(void * mmap)464 explicit BroadcastRing(void* mmap) { 465 CHECK_EQ(0U, reinterpret_cast<uintptr_t>(mmap) % alignof(Mmap)); 466 data_.mmap = reinterpret_cast<Mmap*>(mmap); 467 } 468 469 // Initializes the mmap area header for a new ring. InitializeHeader(uint32_t record_size,uint32_t record_count)470 void InitializeHeader(uint32_t record_size, uint32_t record_count) { 471 constexpr uint32_t kInitialSequence = -256; // Force an early wrap. 472 std::atomic_store_explicit(&header_mmap()->record_size, record_size, 473 std::memory_order_relaxed); 474 std::atomic_store_explicit(&header_mmap()->record_count, record_count, 475 std::memory_order_relaxed); 476 std::atomic_store_explicit(&header_mmap()->head, kInitialSequence, 477 std::memory_order_relaxed); 478 std::atomic_store_explicit(&header_mmap()->tail, kInitialSequence, 479 std::memory_order_relaxed); 480 } 481 482 // Validates ring geometry. 483 // 484 // Ring geometry is validated carefully on import and then cached. This allows 485 // us to avoid out-of-range accesses even if the parameters in the header are 486 // later changed. ValidateGeometry(size_t mmap_size,uint32_t header_record_size,uint32_t header_record_count)487 bool ValidateGeometry(size_t mmap_size, uint32_t header_record_size, 488 uint32_t header_record_count) { 489 set_record_size(header_record_size); 490 set_record_count(header_record_count); 491 492 if (record_size() != header_record_size) return false; 493 if (record_count() != header_record_count) return false; 494 if (record_count() < Traits::kMinRecordCount) return false; 495 if (record_size() < sizeof(Record)) return false; 496 if (record_size() % kRecordAlignment != 0) return false; 497 if (!IsPowerOfTwo(record_count())) return false; 498 499 size_t memory_size = record_count() * record_size(); 500 if (memory_size / record_size() != record_count()) return false; 501 if (memory_size + sizeof(Header) < memory_size) return false; 502 if (memory_size + sizeof(Header) > mmap_size) return false; 503 504 return true; 505 } 506 507 // Copies a record into the ring. 508 // 509 // This is done with relaxed atomics because otherwise it is racy according to 510 // the C++ memory model. This is very low overhead once optimized. PutRecordInternal(const Record * in,RecordStorage * out)511 static inline void PutRecordInternal(const Record* in, RecordStorage* out) { 512 StorageType data[sizeof(Record) / sizeof(StorageType)]; 513 memcpy(data, in, sizeof(*in)); 514 for (size_t i = 0; i < std::extent<decltype(data)>(); ++i) { 515 std::atomic_store_explicit(&out->data[i], data[i], 516 std::memory_order_relaxed); 517 } 518 } 519 520 // Copies a record out of the ring. 521 // 522 // This is done with relaxed atomics because otherwise it is racy according to 523 // the C++ memory model. This is very low overhead once optimized. GetRecordInternal(RecordStorage * in,Record * out)524 static inline void GetRecordInternal(RecordStorage* in, Record* out) { 525 StorageType data[sizeof(Record) / sizeof(StorageType)]; 526 for (size_t i = 0; i < std::extent<decltype(data)>(); ++i) { 527 data[i] = 528 std::atomic_load_explicit(&in->data[i], std::memory_order_relaxed); 529 } 530 memcpy(out, &data, sizeof(*out)); 531 } 532 533 // Converts a record's sequence number into a storage index. SequenceToIndex(uint32_t sequence,uint32_t record_count)534 static uint32_t SequenceToIndex(uint32_t sequence, uint32_t record_count) { 535 return sequence & (record_count - 1); 536 } 537 538 // Computes readable & writable ranges from ring parameters. CalculateGeometry(uint32_t record_count,uint32_t record_size,uint32_t head,uint32_t tail)539 static Geometry CalculateGeometry(uint32_t record_count, uint32_t record_size, 540 uint32_t head, uint32_t tail) { 541 Geometry geometry; 542 geometry.record_count = record_count; 543 geometry.record_size = record_size; 544 DCHECK_EQ(0U, geometry.record_size % kRecordAlignment); 545 geometry.head = head; 546 geometry.tail = tail; 547 geometry.head_index = SequenceToIndex(head, record_count); 548 geometry.tail_index = SequenceToIndex(tail, record_count); 549 geometry.count = geometry.tail - geometry.head; 550 DCHECK_LE(geometry.count, record_count); 551 geometry.space = geometry.record_count - geometry.count; 552 return geometry; 553 } 554 555 // Gets the current ring readable & writable regions. 556 // 557 // This this is always safe from the writing thread since it is the only 558 // thread allowed to update the header. GetGeometry()559 Geometry GetGeometry() const { 560 return CalculateGeometry( 561 record_count(), record_size(), 562 std::atomic_load_explicit(&header_mmap()->head, 563 std::memory_order_relaxed), 564 std::atomic_load_explicit(&header_mmap()->tail, 565 std::memory_order_relaxed)); 566 } 567 568 // Makes space for at least |reserve_count| records. 569 // 570 // There is nothing to prevent overwriting records that have concurrent 571 // readers. We do however ensure that this situation can be detected: the 572 // fence ensures the |head| update will be the first update seen by readers, 573 // and readers check this value after reading and discard data that may have 574 // been concurrently modified. Reserve(uint32_t reserve_count)575 void Reserve(uint32_t reserve_count) { 576 Geometry geometry = GetGeometry(); 577 DCHECK_LE(reserve_count, Traits::kMaxReservedRecords); 578 uint32_t needed = 579 (geometry.space >= reserve_count ? 0 : reserve_count - geometry.space); 580 581 std::atomic_store_explicit(&header_mmap()->head, geometry.head + needed, 582 std::memory_order_relaxed); 583 584 // NB: It is not sufficient to change this to a store-release of |head|. 585 std::atomic_thread_fence(std::memory_order_release); 586 } 587 588 // Makes |publish_count| records visible to readers. 589 // 590 // Space must have been reserved by a previous call to Reserve(). Publish(uint32_t publish_count)591 void Publish(uint32_t publish_count) { 592 Geometry geometry = GetGeometry(); 593 DCHECK_LE(publish_count, geometry.space); 594 std::atomic_store_explicit(&header_mmap()->tail, 595 geometry.tail + publish_count, 596 std::memory_order_release); 597 } 598 599 // Helpers to compute addresses in mmap area. mmap()600 Mmap* mmap() const { return data_.mmap; } header_mmap()601 Header* header_mmap() const { return &data_.mmap->header; } record_mmap_writer(uint32_t index)602 RecordStorage* record_mmap_writer(uint32_t index) const { 603 DCHECK_EQ(sizeof(Record), record_size()); 604 return &data_.mmap->records[index]; 605 } record_mmap_reader(uint32_t index)606 RecordStorage* record_mmap_reader(uint32_t index) const { 607 if (Traits::kUseStaticRecordSize) { 608 return &data_.mmap->records[index]; 609 } else { 610 // Calculate the location of a record in the ring without assuming that 611 // sizeof(Record) == record_size. 612 return reinterpret_cast<RecordStorage*>( 613 reinterpret_cast<char*>(data_.mmap->records) + index * record_size()); 614 } 615 } 616 617 // The following horrifying template gunk enables us to store just the mmap 618 // base pointer for compile-time statically sized rings. Dynamically sized 619 // rings also store the validated copy of the record size & count. 620 // 621 // This boils down to: use a compile time constant if available, and otherwise 622 // load the value that was validated on import from a member variable. 623 template <typename T = Traits> 624 typename std::enable_if<T::kUseStaticRecordSize, uint32_t>::type record_size_internal()625 record_size_internal() const { 626 return sizeof(Record); 627 } 628 629 template <typename T = Traits> 630 typename std::enable_if<!T::kUseStaticRecordSize, uint32_t>::type record_size_internal()631 record_size_internal() const { 632 return data_.record_size; 633 } 634 635 template <typename T = Traits> set_record_size(uint32_t)636 typename std::enable_if<T::kUseStaticRecordSize, void>::type set_record_size( 637 uint32_t /*record_size*/) {} 638 639 template <typename T = Traits> set_record_size(uint32_t record_size)640 typename std::enable_if<!T::kUseStaticRecordSize, void>::type set_record_size( 641 uint32_t record_size) { 642 data_.record_size = record_size; 643 } 644 645 template <typename T = Traits> 646 typename std::enable_if<T::kUseStaticRecordCount, uint32_t>::type record_count_internal()647 record_count_internal() const { 648 return Traits::kStaticRecordCount; 649 } 650 651 template <typename T = Traits> 652 typename std::enable_if<!T::kUseStaticRecordCount, uint32_t>::type record_count_internal()653 record_count_internal() const { 654 return data_.record_count; 655 } 656 657 template <typename T = Traits> 658 typename std::enable_if<T::kUseStaticRecordCount, void>::type set_record_count(uint32_t)659 set_record_count(uint32_t /*record_count*/) const {} 660 661 template <typename T = Traits> 662 typename std::enable_if<!T::kUseStaticRecordCount, void>::type set_record_count(uint32_t record_count)663 set_record_count(uint32_t record_count) { 664 data_.record_count = record_count; 665 } 666 667 // Data we need to store for statically sized rings. 668 struct DataStaticSize { 669 Mmap* mmap = nullptr; 670 }; 671 672 // Data we need to store for dynamically sized rings. 673 struct DataDynamicSize { 674 Mmap* mmap = nullptr; 675 676 // These are cached to make sure misbehaving writers cannot cause 677 // out-of-bounds memory accesses by updating the values in the mmap header. 678 uint32_t record_size = 0; 679 uint32_t record_count = 0; 680 }; 681 682 using DataStaticOrDynamic = 683 typename std::conditional<Traits::kIsStaticSize, DataStaticSize, 684 DataDynamicSize>::type; 685 686 DataStaticOrDynamic data_; 687 }; 688 689 } // namespace dvr 690 } // namespace android 691 692 #endif // ANDROID_DVR_BROADCAST_RING_H_ 693