duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

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