HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
rcu.hpp
1// Copyright Take Vos 2022.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
7#include "wfree_idle_count.hpp"
8#include "unfair_mutex.hpp"
9#include "../utility/utility.hpp"
10#include "../macros.hpp"
11#include <vector>
12#include <tuple>
13#include <mutex>
14#include <memory>
15
16
17
18namespace hi::inline v1 {
19
25template<typename T, typename Allocator = std::allocator<T>>
26class rcu {
27public:
28 using value_type = T;
29 using allocator_type = Allocator;
30
36 constexpr rcu(allocator_type allocator = allocator_type{}) noexcept : _allocator(allocator) {}
37
38 ~rcu() = default;
39 rcu(rcu const&) = delete;
40 rcu(rcu&&) = delete;
41 rcu& operator=(rcu const&) = delete;
42 rcu& operator=(rcu&&) = delete;
43
46 void lock() const noexcept
47 {
48 _idle_count.lock();
49 }
50
53 void unlock() const noexcept
54 {
55 _idle_count.unlock();
56 }
57
63 value_type const *get() const noexcept
64 {
65 return _ptr.load(std::memory_order::acquire);
66 }
67
75 value_type const *unsafe_get() noexcept
76 {
77#if defined(__alpha__) or defined(__alpha) or defined(_M_ALPHA)
78 return _ptr.load(std::memory_order::acquire);
79#else
80 return _ptr.load(std::memory_order::relaxed);
81#endif
82 }
83
89 [[nodiscard]] uint64_t version() const noexcept
90 {
91 return *_idle_count;
92 }
93
98 [[nodiscard]] size_t capacity() const noexcept
99 {
100 hilet lock = std::scoped_lock(_old_ptrs_mutex);
101 return _old_ptrs.size() + not empty();
102 }
103
110 [[nodiscard]] value_type *exchange(value_type *ptr) noexcept
111 {
112 return _ptr.exchange(ptr, std::memory_order::release);
113 }
114
122 [[nodiscard]] value_type *copy(value_type const *ptr) const noexcept
123 {
124 hi_assert_not_null(ptr);
125 value_type *new_ptr = std::allocator_traits<allocator_type>::allocate(_allocator, 1);
126 std::allocator_traits<allocator_type>::construct(_allocator, new_ptr, *ptr);
127 return std::launder(new_ptr);
128 }
129
136 [[nodiscard]] value_type *copy() const noexcept
137 {
138 auto *new_ptr = std::allocator_traits<allocator_type>::allocate(_allocator, 1);
139 lock();
140 value_type const * const ptr = get();
141 hi_assert_not_null(ptr);
142 std::allocator_traits<allocator_type>::construct(_allocator, new_ptr, *ptr);
143 unlock();
144 return std::launder(new_ptr);
145 }
146
151 void abort(value_type *ptr) const noexcept
152 {
155 }
156
161 void commit(value_type *ptr) noexcept
162 {
163 lock();
164 auto *old_ptr = exchange(ptr);
165 auto old_version = version();
166 unlock();
167 add_old_copy(old_version, old_ptr);
168 }
169
180 void emplace(auto&&...args) noexcept
181 {
182 value_type *const new_ptr = std::allocator_traits<allocator_type>::allocate(_allocator, 1);
183 std::allocator_traits<allocator_type>::construct(_allocator, new_ptr, hi_forward(args)...);
184
185 lock();
186 auto *const old_ptr = exchange(new_ptr);
187 hilet old_version = version();
188 unlock();
189
190 add_old_copy(old_version, old_ptr);
191 }
192
193 [[nodiscard]] bool empty() const noexcept
194 {
195 return not to_bool(_ptr.load(std::memory_order::relaxed));
196 }
197
198 explicit operator bool() const noexcept
199 {
200 return not empty();
201 }
202
203 void reset() noexcept
204 {
205 lock();
206 auto *const old_ptr = _ptr.exchange(nullptr, std::memory_order::release);
207 hilet old_version = *_idle_count;
208 unlock();
209
210 add_old_copy(old_version, old_ptr);
211 }
212
213 rcu& operator=(nullptr_t) noexcept
214 {
215 reset();
216 return *this;
217 }
218
228 void add_old_copy(uint64_t old_version, value_type *old_ptr) noexcept
229 {
230 if (not old_ptr) {
231 return;
232 }
233
234 hilet new_version = version();
235
236 hilet lock = std::scoped_lock(_old_ptrs_mutex);
237 _old_ptrs.emplace_back(old_version, old_ptr);
238
239 // Destroy all objects from previous idle-count versions.
240 auto it = _old_ptrs.begin();
241 while (it != _old_ptrs.end() and it->first < new_version) {
243 std::allocator_traits<allocator_type>::deallocate(_allocator, it->second, 1);
244 ++it;
245 }
246 _old_ptrs.erase(_old_ptrs.begin(), it);
247 }
248
249private:
250 std::atomic<value_type *> _ptr = nullptr;
251 mutable wfree_idle_count _idle_count;
252
253 mutable allocator_type _allocator;
254 mutable unfair_mutex _old_ptrs_mutex;
256};
257
258} // namespace hi::inline v1
259
Definition of the unfair_mutex.
DOXYGEN BUG.
Definition algorithm.hpp:16
constexpr Out narrow_cast(In const &rhs) noexcept
Cast numeric values without loss of precision.
Definition cast.hpp:377
Read-copy-update.
Definition rcu.hpp:26
constexpr rcu(allocator_type allocator=allocator_type{}) noexcept
Construct a new rcu object.
Definition rcu.hpp:36
void add_old_copy(uint64_t old_version, value_type *old_ptr) noexcept
Add an old copy.
Definition rcu.hpp:228
void unlock() const noexcept
Unlock the rcu pointer for reading.
Definition rcu.hpp:53
void commit(value_type *ptr) noexcept
Commit the copied value.
Definition rcu.hpp:161
value_type * copy() const noexcept
Create a copy of the value.
Definition rcu.hpp:136
value_type const * get() const noexcept
get the rcu-pointer.
Definition rcu.hpp:63
void lock() const noexcept
Lock the rcu pointer for reading.
Definition rcu.hpp:46
value_type * exchange(value_type *ptr) noexcept
Exchange the rcu-pointers.
Definition rcu.hpp:110
value_type const * unsafe_get() noexcept
get the rcu-pointer.
Definition rcu.hpp:75
void emplace(auto &&...args) noexcept
Emplace a new value.
Definition rcu.hpp:180
size_t capacity() const noexcept
Number of objects that are currently allocated.
Definition rcu.hpp:98
uint64_t version() const noexcept
The version of the lock.
Definition rcu.hpp:89
void abort(value_type *ptr) const noexcept
Abort a copy.
Definition rcu.hpp:151
value_type * copy(value_type const *ptr) const noexcept
Create a copy of the value at the given pointer.
Definition rcu.hpp:122
Counts how many times a critical section was idle.
Definition wfree_idle_count.hpp:40
T allocate(T... args)
T construct(T... args)
T deallocate(T... args)
T destroy(T... args)
T lock(T... args)