6#ifndef XENIUM_LEFT_RIGHT_HPP
7#define XENIUM_LEFT_RIGHT_HPP
16#pragma warning(disable: 4324)
48 right(std::move(source))
60 left(std::move(left)),
61 right(std::move(right))
82 template<
typename Func>
83 auto read(Func&& func)
const {
84 read_guard guard(*
this);
86 const T& inst = lr_indicator.load(std::memory_order_seq_cst) == READ_LEFT ? left : right;
99 template<
typename Func>
101 std::lock_guard<std::mutex> lock(writer_mutex);
102 assert(lr_indicator.load() == version_index.load());
103 if (lr_indicator.load(std::memory_order_relaxed) == READ_LEFT) {
106 lr_indicator.store(READ_RIGHT, std::memory_order_seq_cst);
107 toggle_version_and_wait();
112 lr_indicator.store(READ_LEFT, std::memory_order_seq_cst);
113 toggle_version_and_wait();
118 struct alignas(64) read_indicator {
121 counter.fetch_add(1, std::memory_order_seq_cst);
125 counter.fetch_sub(1, std::memory_order_release);
134 return counter.load(std::memory_order_seq_cst) == 0;
137 std::atomic<uint64_t> counter{0};
142 indicator(inst.get_read_indicator(inst.version_index.load(std::memory_order_relaxed)))
146 ~read_guard() { indicator.depart(); }
148 read_indicator& indicator;
150 friend struct read_guard;
152 void toggle_version_and_wait(
void) {
153 const int current_version = version_index.load(std::memory_order_relaxed);
154 const int current_idx = current_version & 0x1;
155 const int next_idx = (current_version + 1) & 0x1;
157 wait_for_readers(next_idx);
158 version_index.store(next_idx, std::memory_order_relaxed);
159 wait_for_readers(current_idx);
162 void wait_for_readers(
int idx) {
163 auto& indicator = get_read_indicator(idx);
164 while (!indicator.empty())
165 std::this_thread::yield();
168 read_indicator& get_read_indicator(
int idx)
const {
169 assert(idx == 0 || idx == 1);
171 return read_indicator1;
172 return read_indicator2;
175 static constexpr int READ_LEFT = 0;
176 static constexpr int READ_RIGHT = 1;
179 std::mutex writer_mutex;
180 std::atomic<int> version_index{0} ;
181 std::atomic<int> lr_indicator { READ_LEFT };
183 mutable read_indicator read_indicator1;
186 mutable read_indicator read_indicator2;
Generic implementation of the LeftRight algorithm proposed by Ramalhete and Correia [RC15].
Definition: left_right.hpp:38
left_right(T source)
Initialize the two underlying T instances with the specified source.
Definition: left_right.hpp:46
auto read(Func &&func) const
Performs a read operation on the active instance using the specified functor.
Definition: left_right.hpp:83
void update(Func &&func)
Performs an update operation on both underlying instances using the specified functor.
Definition: left_right.hpp:100
left_right()=default
Default constructs both underlying instances.
left_right(T left, T right)
Initializes the two underlying instances withe the specified sources.
Definition: left_right.hpp:59