18 #ifndef CSLIBGUARDED_LR_GUARDED_H
19 #define CSLIBGUARDED_LR_GUARDED_H
50 template <
typename T,
typename Mutex = std::mutex>
57 using shared_handle = std::unique_ptr<
const T, shared_deleter>;
63 template <
typename... Us>
64 lr_guarded(Us &&... data);
78 template <
typename Func>
79 void modify(Func && f);
84 [[nodiscard]] shared_handle lock_shared()
const;
89 [[nodiscard]] shared_handle try_lock_shared()
const;
94 template <
class Duration>
95 [[nodiscard]] shared_handle try_lock_shared_for(
const Duration & duration)
const;
100 template <
class TimePoint>
101 [[nodiscard]] shared_handle try_lock_shared_until(
const TimePoint & timepoint)
const;
107 using pointer =
const T *;
109 shared_deleter() : m_readingCount(
nullptr) {}
111 shared_deleter(
const shared_deleter &) =
delete;
112 shared_deleter& operator=(
const shared_deleter&) =
delete;
114 shared_deleter(shared_deleter && other)
115 : m_readingCount(other.m_readingCount)
117 other.m_readingCount =
nullptr;
120 shared_deleter& operator=(shared_deleter&& other) & {
121 m_readingCount = other.m_readingCount;
122 other.m_readingCount =
nullptr;
125 shared_deleter(std::atomic<
int> & readingCount)
126 : m_readingCount(&readingCount)
130 void operator()(
const T * ptr) {
131 if (ptr && m_readingCount) {
137 std::atomic<
int> * m_readingCount;
142 std::atomic<
bool> m_readingLeft;
143 std::atomic<
bool> m_countingLeft;
144 mutable std::atomic<
int> m_leftReadCount;
145 mutable std::atomic<
int> m_rightReadCount;
146 mutable Mutex m_writeMutex;
149 template <
typename T,
typename M>
150 template <
typename... Us>
151 lr_guarded<T, M>::lr_guarded(Us &&... data)
152 : m_left(std::forward<Us>(data)...), m_right(m_left), m_readingLeft(
true), m_countingLeft(
true),
153 m_leftReadCount(0), m_rightReadCount(0)
157 template <
typename T,
typename M>
158 template <
typename Func>
159 void lr_guarded<T, M>::modify(Func && func)
163 std::lock_guard<M> lock(m_writeMutex);
165 T *firstWriteLocation;
166 T *secondWriteLocation;
168 bool local_readingLeft = m_readingLeft.load();
170 if (local_readingLeft) {
171 firstWriteLocation = &m_right;
172 secondWriteLocation = &m_left;
174 firstWriteLocation = &m_left;
175 secondWriteLocation = &m_right;
179 func(*firstWriteLocation);
181 *firstWriteLocation = *secondWriteLocation;
185 m_readingLeft.store(! local_readingLeft);
187 bool local_countingLeft = m_countingLeft.load();
189 if (local_countingLeft) {
190 while (m_rightReadCount.load() != 0) {
191 std::this_thread::yield();
195 while (m_leftReadCount.load() != 0) {
196 std::this_thread::yield();
200 m_countingLeft.store(!local_countingLeft);
202 if (local_countingLeft) {
203 while (m_leftReadCount.load() != 0) {
204 std::this_thread::yield();
208 while (m_rightReadCount.load() != 0) {
209 std::this_thread::yield();
214 func(*secondWriteLocation);
216 *secondWriteLocation = *firstWriteLocation;
221 template <
typename T,
typename M>
222 auto lr_guarded<T, M>::lock_shared()
const -> shared_handle
224 if (m_countingLeft) {
228 return shared_handle(&m_left, shared_deleter(m_leftReadCount));
230 return shared_handle(&m_right, shared_deleter(m_leftReadCount));
237 return shared_handle(&m_left, shared_deleter(m_rightReadCount));
239 return shared_handle(&m_right, shared_deleter(m_rightReadCount));
244 template <
typename T,
typename M>
245 auto lr_guarded<T, M>::try_lock_shared()
const -> shared_handle
247 return lock_shared();
250 template <
typename T,
typename M>
251 template <
typename Duration>
252 auto lr_guarded<T, M>::try_lock_shared_for(
const Duration &)
const -> shared_handle
254 return lock_shared();
257 template <
typename T,
typename M>
258 template <
typename TimePoint>
259 auto lr_guarded<T, M>::try_lock_shared_until(
const TimePoint &)
const -> shared_handle
261 return lock_shared();