6 #ifndef XENIUM_LEFT_RIGHT_HPP
7 #define XENIUM_LEFT_RIGHT_HPP
16 #pragma warning(disable : 4324) // structure was padded due to alignment specifier
46 explicit left_right(T source) : _left(source), _right(std::move(source)) {}
56 left_right(T left, T right) : _left(std::move(left)), _right(std::move(right)) {}
76 template <
typename Func>
77 auto read(Func&& func)
const {
78 read_guard guard(*
this);
80 const T& inst = _lr_indicator.load(std::memory_order_seq_cst) == READ_LEFT ? _left : _right;
93 template <
typename Func>
95 std::lock_guard<std::mutex> lock(_writer_mutex);
96 assert(_lr_indicator.load() == _version_index.load());
97 if (_lr_indicator.load(std::memory_order_relaxed) == READ_LEFT) {
100 _lr_indicator.store(READ_RIGHT, std::memory_order_seq_cst);
101 toggle_version_and_wait();
106 _lr_indicator.store(READ_LEFT, std::memory_order_seq_cst);
107 toggle_version_and_wait();
113 struct alignas(64) read_indicator {
116 _counter.fetch_add(1, std::memory_order_seq_cst);
120 _counter.fetch_sub(1, std::memory_order_release);
129 return _counter.load(std::memory_order_seq_cst) == 0;
133 std::atomic<uint64_t> _counter{0};
138 _indicator(inst.get_read_indicator(inst._version_index.load(std::memory_order_relaxed))) {
141 ~read_guard() { _indicator.depart(); }
144 read_indicator& _indicator;
146 friend struct read_guard;
148 void toggle_version_and_wait() {
149 const int current_version = _version_index.load(std::memory_order_relaxed);
150 const int current_idx = current_version & 0x1;
151 const int next_idx = (current_version + 1) & 0x1;
153 wait_for_readers(next_idx);
154 _version_index.store(next_idx, std::memory_order_relaxed);
155 wait_for_readers(current_idx);
158 void wait_for_readers(
int idx) {
159 auto& indicator = get_read_indicator(idx);
160 while (!indicator.empty()) {
161 std::this_thread::yield();
165 read_indicator& get_read_indicator(
int idx)
const {
166 assert(idx == 0 || idx == 1);
168 return _read_indicator1;
170 return _read_indicator2;
173 static constexpr
int READ_LEFT = 0;
174 static constexpr
int READ_RIGHT = 1;
177 std::mutex _writer_mutex;
178 std::atomic<int> _version_index{0};
179 std::atomic<int> _lr_indicator{READ_LEFT};
181 mutable read_indicator _read_indicator1;
184 mutable read_indicator _read_indicator2;