cubeb_triple_buffer.h (2562B)
1 /* 2 * Copyright © 2022 Mozilla Foundation 3 * 4 * This program is made available under an ISC-style license. See the 5 * accompanying file LICENSE for details. 6 */ 7 8 /** 9 * Adapted and ported to C++ from https://crates.io/crates/triple_buffer 10 */ 11 12 #ifndef CUBEB_TRIPLE_BUFFER 13 #define CUBEB_TRIPLE_BUFFER 14 15 #include <atomic> 16 17 // Single producer / single consumer wait-free triple buffering 18 // implementation, for when a producer wants to publish data to a consumer 19 // without blocking, but when a queue is wastefull, because it's OK for the 20 // consumer to miss data updates. 21 template <typename T> class triple_buffer { 22 public: 23 // Write a new value into the triple buffer. Returns true if a value was 24 // overwritten. 25 // Producer-side only. 26 bool write(T & input) 27 { 28 storage[input_idx] = input; 29 return publish(); 30 } 31 // Get the latest value from the triple buffer. 32 // Consumer-side only. 33 T & read() 34 { 35 update(); 36 return storage[output_idx]; 37 } 38 // Returns true if a new value has been published by the consumer without 39 // having been consumed yet. 40 // Consumer-side only. 41 bool updated() 42 { 43 return (shared_state.load(std::memory_order_relaxed) & BACK_DIRTY_BIT) != 0; 44 } 45 // Reset state and indices to initial values. 46 void invalidate() 47 { 48 shared_state.store(0, std::memory_order_release); 49 input_idx = 1; 50 output_idx = 2; 51 } 52 53 private: 54 // Publish a value to the consumer. Returns true if the data was overwritten 55 // without having been read. 56 bool publish() 57 { 58 auto former_back_idx = shared_state.exchange(input_idx | BACK_DIRTY_BIT, 59 std::memory_order_acq_rel); 60 input_idx = former_back_idx & BACK_INDEX_MASK; 61 return (former_back_idx & BACK_DIRTY_BIT) != 0; 62 } 63 // Get a new value from the producer, if a new value has been produced. 64 bool update() 65 { 66 bool was_updated = updated(); 67 if (was_updated) { 68 auto former_back_idx = 69 shared_state.exchange(output_idx, std::memory_order_acq_rel); 70 output_idx = former_back_idx & BACK_INDEX_MASK; 71 } 72 return was_updated; 73 } 74 T storage[3]; 75 // Mask used to extract back-buffer index 76 const uint8_t BACK_INDEX_MASK = 0b11; 77 // Bit set by producer to signal updates 78 const uint8_t BACK_DIRTY_BIT = 0b100; 79 // Shared state: a dirty bit, and an index. 80 std::atomic<uint8_t> shared_state = {0}; 81 // Output index, private to the consumer. 82 uint8_t output_idx = 1; 83 // Input index, private to the producer. 84 uint8_t input_idx = 2; 85 }; 86 87 #endif // CUBEB_TRIPLE_BUFFER