CsLibGuarded  1.4.1
cs_cow_guarded.h
1 /***********************************************************************
2 *
3 * Copyright (c) 2016-2024 Ansel Sermersheim
4 *
5 * This file is part of CsLibGuarded.
6 *
7 * CsLibGuarded is free software, released under the BSD 2-Clause license.
8 * For license details refer to LICENSE provided with this project.
9 *
10 * CsLibGuarded is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 *
14 * https://opensource.org/licenses/BSD-2-Clause
15 *
16 ***********************************************************************/
17 
18 #ifndef CSLIBGUARDED_COW_GUARDED_H
19 #define CSLIBGUARDED_COW_GUARDED_H
20 
21 #include "cs_lr_guarded.h"
22 
23 #include <atomic>
24 #include <memory>
25 #include <mutex>
26 
27 namespace libguarded
28 {
29 
30 // 27 comment line(s) omitted
57 template <typename T, typename Mutex = std::mutex>
58 class cow_guarded
59 {
60  private:
61  class deleter;
62  class shared_deleter;
63 
64  public:
65  class handle;
66  using shared_handle = std::shared_ptr<const T>;
67 
68  // 4 comment line(s) omitted
72  template <typename... Us>
73  cow_guarded(Us &&... data);
74 
75  // 6 comment line(s) omitted
81  handle lock();
82 
83  // 7 comment line(s) omitted
90  handle try_lock();
91 
92  // 14 comment line(s) omitted
106  template <class Duration>
107  handle try_lock_for(const Duration &duration);
108 
109  // 13 comment line(s) omitted
122  template <class TimePoint>
123  handle try_lock_until(const TimePoint &timepoint);
124 
125  // 4 comment line(s) omitted
129  shared_handle lock_shared() const;
130 
131  // 4 comment line(s) omitted
135  shared_handle try_lock_shared() const;
136 
137  // 4 comment line(s) omitted
141  template <class Duration>
142  shared_handle try_lock_shared_for(const Duration &duration) const;
143 
144  // 4 comment line(s) omitted
148  template <class TimePoint>
149  shared_handle try_lock_shared_until(const TimePoint &timepoint) const;
150 
151  private:
152  class deleter
153  {
154  public:
155  using pointer = T *;
156 
157  deleter() = default;
158 
159  deleter(std::unique_lock<Mutex> &&lock, cow_guarded &guarded)
160  : m_lock(std::move(lock)), m_guarded(&guarded), m_cancelled(false)
161  {
162  }
163 
164  void cancel() {
165  m_cancelled = true;
166 
167  if (m_lock.owns_lock()) {
168  m_lock.unlock();
169  }
170  }
171 
172  void operator()(T *ptr) {
173  if (m_cancelled) {
174  delete ptr;
175 
176  } else if (ptr && m_guarded) {
177  std::shared_ptr<const T> newPtr(ptr);
178 
179  m_guarded->m_data.modify([newPtr](std::shared_ptr<const T> &ptr) { ptr = newPtr; });
180  }
181 
182  if (m_lock.owns_lock()) {
183  m_lock.unlock();
184  }
185  }
186 
187  private:
188  std::unique_lock<Mutex> m_lock;
189  cow_guarded *m_guarded;
190  bool m_cancelled;
191  };
192 
193  public:
194  // 3 comment line(s) omitted
197  class handle : public std::unique_ptr<T, deleter>
198  {
199  public:
200  using std::unique_ptr<T, deleter>::unique_ptr;
201 
202  // 3 comment line(s) omitted
205  void cancel() {
206  this->get_deleter().cancel();
207  this->reset();
208  }
209  };
210 
211  private:
212  mutable lr_guarded<std::shared_ptr<const T>> m_data;
213  mutable Mutex m_writeMutex;
214 };
215 
216 template <typename T, typename M>
217 template <typename... Us>
218 cow_guarded<T, M>::cow_guarded(Us &&... data)
219  : m_data(std::make_shared<T>(std::forward<Us>(data)...))
220 {
221 }
222 
223 template <typename T, typename M>
224 auto cow_guarded<T, M>::lock() -> handle
225 {
226  std::unique_lock<M> guard(m_writeMutex);
227 
228  auto data(m_data.lock_shared());
229  std::unique_ptr<T> val(new T(**data));
230  data.reset();
231 
232  return handle(val.release(), deleter(std::move(guard), *this));
233 }
234 
235 template <typename T, typename M>
236 auto cow_guarded<T, M>::try_lock() -> handle
237 {
238  std::unique_lock<M> guard(m_writeMutex, std::try_to_lock);
239 
240  if (!guard.owns_lock()) {
241  return handle();
242  }
243 
244  // lr_guarded::lock_shared cannot block or fail
245  auto data(m_data.lock_shared());
246 
247  std::unique_ptr<T> val(new T(**data));
248  data.reset();
249 
250  return handle(val.release(), deleter(std::move(guard), *this));
251 }
252 
253 template <typename T, typename M>
254 template <typename Duration>
255 auto cow_guarded<T, M>::try_lock_for(const Duration &duration) -> handle
256 {
257  std::unique_lock<M> guard(m_writeMutex, duration);
258 
259  if (!guard.owns_lock()) {
260  return handle();
261  }
262 
263  // lr_guarded::lock_shared cannot block or fail
264  auto data = m_data.lock_shared();
265 
266  std::unique_ptr<T> val(new T(**data));
267  data.reset();
268 
269  return handle(val.release(), deleter(std::move(guard), *this));
270 }
271 
272 template <typename T, typename M>
273 template <typename TimePoint>
274 auto cow_guarded<T, M>::try_lock_until(const TimePoint &timepoint) -> handle
275 {
276  std::unique_lock<M> guard(m_writeMutex, timepoint);
277 
278  if (! guard.owns_lock()) {
279  return handle();
280  }
281 
282  // lr_guarded::lock_shared cannot block or fail
283  auto data(m_data.lock_shared());
284 
285  std::unique_ptr<T> val(new T(**data));
286  data.reset();
287 
288  return handle(val.release(), deleter(std::move(guard), *this));
289 }
290 
291 template <typename T, typename M>
292 auto cow_guarded<T, M>::lock_shared() const -> shared_handle
293 {
294  auto lock = m_data.lock_shared();
295  return *lock;
296 }
297 
298 template <typename T, typename M>
299 auto cow_guarded<T, M>::try_lock_shared() const -> shared_handle
300 {
301  shared_handle retval;
302 
303  auto lock = m_data.try_lock_shared();
304  if (lock) {
305  retval = *lock;
306  }
307 
308  return retval;
309 }
310 
311 template <typename T, typename M>
312 template <typename Duration>
313 auto cow_guarded<T, M>::try_lock_shared_for(const Duration &duration) const -> shared_handle
314 {
315  shared_handle retval;
316 
317  auto lock = m_data.try_lock_shared_for(duration);
318  if (lock) {
319  retval = *lock;
320  }
321 
322  return retval;
323 }
324 
325 template <typename T, typename M>
326 template <typename TimePoint>
327 auto cow_guarded<T, M>::try_lock_shared_until(const TimePoint &timepoint) const -> shared_handle
328 {
329  shared_handle retval;
330 
331  auto lock = m_data.try_lock_shared_until(timepoint);
332  if (lock) {
333  retval = *lock;
334  }
335 
336  return retval;
337 }
338 
339 } // namespace libguarded
340 
341 #endif