18 #ifndef CSLIBGUARDED_DEFERRED_GUARDED_H
19 #define CSLIBGUARDED_DEFERRED_GUARDED_H
27 #include <shared_mutex>
33 typename std::add_lvalue_reference<T>::type declref();
48 template <
typename T,
typename M = std::shared_timed_mutex>
49 class deferred_guarded
55 using shared_handle = std::unique_ptr<
const T, shared_deleter>;
57 template <
typename... Us>
58 deferred_guarded(Us &&... data);
60 template <
typename Func>
61 void modify_detach(Func && func);
63 template <
typename Func>
64 [[nodiscard]]
auto modify_async(Func && func) ->
65 typename std::future<
decltype(std::declval<Func>()(declref<T>()))>;
67 [[nodiscard]] shared_handle lock_shared()
const;
68 [[nodiscard]] shared_handle try_lock_shared()
const;
70 template <
class Duration>
71 [[nodiscard]] shared_handle try_lock_shared_for(
const Duration & duration)
const;
73 template <
class TimePoint>
74 [[nodiscard]] shared_handle try_lock_shared_until(
const TimePoint & timepoint)
const;
80 using pointer =
const T *;
82 shared_deleter(M & mutex)
83 : m_deleter_mutex(mutex)
87 void operator()(
const T * ptr)
90 m_deleter_mutex.unlock_shared();
98 void do_pending_writes()
const;
103 mutable std::atomic<
bool> m_pendingWrites;
105 mutable plain_guarded<std::vector<std::packaged_task<
void(T &)>>> m_pendingList;
108 template <
typename T,
typename M>
109 template <
typename... Us>
110 deferred_guarded<T, M>::deferred_guarded(Us &&... data)
111 : m_obj(std::forward<Us>(data)...), m_pendingWrites(
false)
115 template <
typename T,
typename M>
116 template <
typename Func>
117 void deferred_guarded<T, M>::modify_detach(Func && func)
119 std::unique_lock<M> lock(m_mutex, std::try_to_lock);
121 if (lock.owns_lock()) {
123 if (m_pendingWrites.load()) {
124 std::vector<std::packaged_task<
void(T &)>> localPending;
125 m_pendingWrites.store(
false);
126 swap(localPending, *(m_pendingList.lock()));
128 for (
auto & f : localPending) {
136 m_pendingList.lock()->push_back(std::packaged_task<
void(T &)>(std::forward<Func>(func)));
137 m_pendingWrites.store(
true);
141 template <
typename Ret,
typename Func,
typename T>
142 auto call_returning_future(Func & func, T & data) ->
143 typename std::enable_if<!std::is_same<Ret,
void>::value, std::future<Ret>>::type
145 std::promise<Ret> promise;
148 promise.set_value(func(data));
150 promise.set_exception(std::current_exception());
153 return promise.get_future();
156 template <
typename Ret,
typename Func,
typename T>
157 auto call_returning_future(Func & func, T & data) ->
158 typename std::enable_if<std::is_same<Ret,
void>::value, std::future<Ret>>::type
160 std::promise<Ret> promise;
166 promise.set_exception(std::current_exception());
169 return promise.get_future();
172 template <
typename Ret,
typename T,
typename Func>
173 auto package_task_void(Func && func) ->
174 typename std::enable_if<std::is_same<Ret,
void>::value,
175 std::pair<std::packaged_task<
void(T &)>, std::future<
void>>>::type
177 std::packaged_task<
void(T &)> task(std::forward<Func>(func));
178 std::future<
void> task_future(task.get_future());
180 return std::make_pair(std::move(task), std::move(task_future));
183 template <
typename Ret,
typename T,
typename Func>
184 auto package_task_void(Func && func) ->
185 typename std::enable_if<!std::is_same<Ret,
void>::value,
186 std::pair<std::packaged_task<
void(T &)>, std::future<T>>>::type
188 std::packaged_task<Ret(T &)> task(std::forward<Func>(func));
189 std::future<Ret> task_future(task.get_future());
191 return std::make_pair(std::packaged_task<
void(T &)>(std::move(task)), std::move(task_future));
194 template <
typename T,
typename M>
195 template <
typename Func>
196 auto deferred_guarded<T, M>::modify_async(Func && func) ->
197 typename std::future<
decltype(std::declval<Func>()(declref<T>()))>
199 using return_t =
decltype(func(m_obj));
200 using future_t = std::future<
decltype(func(m_obj))>;
203 std::unique_lock<M> lock(m_mutex, std::try_to_lock);
205 if (lock.owns_lock()) {
206 if (m_pendingWrites.load()) {
207 std::vector<std::packaged_task<
void(T &)>> localPending;
209 m_pendingWrites.store(
false);
210 swap(localPending, *(m_pendingList.lock()));
211 for (
auto & f : localPending) {
216 retval = call_returning_future<return_t>(func, m_obj);
219 std::pair<std::packaged_task<
void(T &)>, std::future<return_t>> task_future =
220 package_task_void<return_t, T>(std::forward<Func>(func));
222 retval = std::move(task_future.second);
224 m_pendingList.lock()->push_back(std::move(task_future.first));
225 m_pendingWrites.store(
true);
231 template <
typename T,
typename M>
232 void deferred_guarded<T, M>::do_pending_writes()
const
234 if (m_pendingWrites.load()) {
236 std::unique_lock<M> lock(m_mutex, std::try_to_lock);
238 if (lock.owns_lock()) {
239 if (m_pendingWrites.load()) {
240 std::vector<std::packaged_task<
void(T &)>> localPending;
242 m_pendingWrites.store(
false);
243 swap(localPending, *(m_pendingList.lock()));
245 for (
auto & f : localPending) {
253 template <
typename T,
typename M>
254 auto deferred_guarded<T, M>::lock_shared()
const -> shared_handle
257 m_mutex.lock_shared();
259 return shared_handle(&m_obj, shared_deleter(m_mutex));
262 template <
typename T,
typename M>
263 auto deferred_guarded<T, M>::try_lock_shared()
const -> shared_handle
266 if (m_mutex.try_lock_shared()) {
267 return shared_handle(&m_obj, shared_deleter(m_mutex));
269 return shared_handle(
nullptr, shared_deleter(m_mutex));
273 template <
typename T,
typename M>
274 template <
typename Duration>
275 auto deferred_guarded<T, M>::try_lock_shared_for(
const Duration & d)
const -> shared_handle
278 if (m_mutex.try_lock_shared_for(d)) {
279 return shared_handle(&m_obj, shared_deleter(m_mutex));
281 return shared_handle(
nullptr, shared_deleter(m_mutex));
285 template <
typename T,
typename M>
286 template <
typename TimePoint>
287 auto deferred_guarded<T, M>::try_lock_shared_until(
const TimePoint & tp)
const -> shared_handle
290 if (m_mutex.try_lock_shared_until(tp)) {
291 return shared_handle(&m_obj, shared_deleter(m_mutex));
293 return shared_handle(
nullptr, shared_deleter(m_mutex));