RWLEnvelope 0.0.0
Thread-safe envelope wrapper using reader-writer locks
Loading...
Searching...
No Matches
RWLEnvelope.hpp
Go to the documentation of this file.
1/*
2 RWLEnvelope
3 Reader Writer lock wrapper for object access
4 Repo: https://github.com/SiddiqSoft/RWLEnvelope
5
6 BSD 3-Clause License
7
8 Copyright (c) 2021, Siddiq Software LLC
9 All rights reserved.
10
11 Redistribution and use in source and binary forms, with or without
12 modification, are permitted provided that the following conditions are met:
13
14 1. Redistributions of source code must retain the above copyright notice, this
15 list of conditions and the following disclaimer.
16
17 2. Redistributions in binary form must reproduce the above copyright notice,
18 this list of conditions and the following disclaimer in the documentation
19 and/or other materials provided with the distribution.
20
21 3. Neither the name of the copyright holder nor the names of its
22 contributors may be used to endorse or promote products derived from
23 this software without specific prior written permission.
24
25 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
26 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
29 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
33 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 */
36
37#pragma once
38
59
60#ifndef RWLENVELOPE_HPP
61#define RWLENVELOPE_HPP
62
63#include <functional>
64#include <shared_mutex>
65#include <tuple>
66#include <utility>
67#include <concepts>
68#include <exception>
69#include <type_traits>
70
71#if !__has_cpp_attribute(nodiscard)
72#error "We really should have [[nodiscard]]"
73#endif
74
75#if !__cpp_lib_shared_mutex
76#error "Must have <shared_mutex>"
77#endif
78
79namespace siddiqsoft
80{
91 template <typename ContainerType, typename Callback, typename... Args>
92 concept ObserveCallbackNoexcept = requires(Callback f, const ContainerType& item, Args... args) {
93 { f(item, std::forward<Args>(args)...) } noexcept;
94 };
95
106 template <typename ContainerType, typename Callback, typename... Args>
107 concept MutateCallbackNoexcept = requires(Callback f, ContainerType& item, Args... args) {
108 { f(item, std::forward<Args>(args)...) } noexcept;
109 };
110
111
178 template <typename ContainerType>
179 requires std::copy_constructible<ContainerType>
180 class RWLEnvelope
181 {
182 using RWLock = std::unique_lock<std::shared_mutex>;
183 using RLock = std::shared_lock<std::shared_mutex>;
184
185 private:
190 struct rone
191 {
193 uint64_t& m_Dest;
194
200 rone(uint64_t& dest)
201 : m_Dest(dest)
202 {
203 }
204
206 ~rone() { ++m_Dest; }
207 };
208
209 public:
221 explicit RWLEnvelope()
222 : _item(ContainerType {})
223 {
224 }
225
226
241 explicit RWLEnvelope(RWLEnvelope<ContainerType>&& src) noexcept
242 {
243 try
244 {
245 // Execute within the lock of the source object
246 if (auto [o, wl] = src.writeLock(); wl)
247 {
248 // Move the internal data from source
249 _item = std::move(o);
250 // Transfer the counter from source to this object
251 _rwa = std::exchange(src._rwa, 0);
252 }
253 }
254 catch (...)
255 {
256 // cannot throw
257 }
258 }
259
260
273 explicit RWLEnvelope(ContainerType&& src)
274 : _item(std::move(src))
275 {
276 //_item = std::move(src);
277 }
278
279
296 template<typename T>
297 explicit RWLEnvelope(std::initializer_list<T> init)
298 {
299 _item = ContainerType(init);
300 }
301
302
317 void reassign(ContainerType&& src)
318 {
319 RWLock myWriterLock(_sMutex);
320 _item = std::move(src);
321 }
322
323
338 explicit RWLEnvelope(const ContainerType& arg)
339 {
340 RWLock myWriterLock(_sMutex);
341 _item = arg;
342 }
343
344
350 RWLEnvelope& operator=(RWLEnvelope const&) = delete;
351
352
375 template <typename Callback, typename... Args>
376 requires ObserveCallbackNoexcept<ContainerType, Callback, Args...>
377 auto observe(Callback cbf, Args&&... args) const
378 {
379 RLock myLock(_sMutex);
380 return cbf(_item, std::forward<Args>(args)...);
381 }
382
383
412 // NOLINTBEGIN(clang-diagnostic-return-type)
413 template <typename Callback, typename... Args>
414 requires MutateCallbackNoexcept<ContainerType, Callback, Args...>
415 auto mutate(Callback cbf, Args&&... args)
416 {
417 RWLock myWriterLock(_sMutex);
418 rone d(_rwa); // we increment the housekeeping counter on each callback
419
420 try
421 {
422 return cbf(_item, std::forward<Args>(args)...);
423 }
424 catch (...)
425 {
426 }
427 }
428 // NOLINTEND(clang-diagnostic-return-type)
429
430
447 [[nodiscard]] ContainerType snapshot() const
448 {
449 RLock myLock(_sMutex);
450 return _item;
451 }
452
453
478 [[nodiscard]] std::tuple<const ContainerType&, RLock> readLock() { return {std::ref(_item), RLock(_sMutex)}; }
479
480
503 [[nodiscard]] std::tuple<ContainerType&, RWLock> writeLock() { return {std::ref(_item), RWLock(_sMutex)}; }
504
505
506#ifdef INCLUDE_NLOHMANN_JSON_HPP_
526 public:
527 operator nlohmann::json() const
528 {
529 RLock myLock(_sMutex);
530 return {{"storage", _item}, {"_typver", "RWLEnvelope/1.5.1"}, {"readWriteActions", _rwa}};
531 }
532#endif
533
534 private:
539 ContainerType _item;
540
545 mutable std::shared_mutex _sMutex {};
546
555 uint64_t _rwa {0};
556 };
557} // namespace siddiqsoft
558
559#endif // !RWLENVELOPE_HPP
RWLEnvelope & operator=(RWLEnvelope const &)=delete
Copy assignment operator (deleted).
Concept to enforce that callbacks do not throw exceptions (for mutable access).
Concept to enforce that callbacks do not throw exceptions (for const/read-only access).