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/module.hpp"
10#include <vector>
11#include <tuple>
12#include <mutex>
13#include <memory>
14
15namespace hi::inline v1 {
16
22template<typename T, typename Allocator = std::allocator<T>>
23class rcu {
24public:
25 using value_type = T;
26 using allocator_type = Allocator;
27
33 constexpr rcu(allocator_type allocator = allocator_type{}) noexcept : _allocator(allocator) {}
34
35 ~rcu() = default;
36 rcu(rcu const&) = delete;
37 rcu(rcu&&) = delete;
38 rcu& operator=(rcu const&) = delete;
39 rcu& operator=(rcu&&) = delete;
40
43 void lock() const noexcept
44 {
45 _idle_count.lock();
46 }
47
50 void unlock() const noexcept
51 {
52 _idle_count.unlock();
53 }
54
60 value_type const *get() const noexcept
61 {
62 return _ptr.load(std::memory_order::acquire);
63 }
64
72 value_type const *unsafe_get() noexcept
73 {
74#if defined(__alpha__) or defined(__alpha) or defined(_M_ALPHA)
75 return _ptr.load(std::memory_order::acquire);
76#else
77 return _ptr.load(std::memory_order::relaxed);
78#endif
79 }
80
86 [[nodiscard]] uint64_t version() const noexcept
87 {
88 return *_idle_count;
89 }
90
95 [[nodiscard]] size_t capacity() const noexcept
96 {
97 hilet lock = std::scoped_lock(_old_ptrs_mutex);
98 return _old_ptrs.size() + not empty();
99 }
100
107 [[nodiscard]] value_type *exchange(value_type *ptr) noexcept
108 {
109 return _ptr.exchange(ptr, std::memory_order::release);
110 }
111
119 [[nodiscard]] value_type *copy(value_type const *ptr) const noexcept
120 {
122 value_type *new_ptr = std::allocator_traits<allocator_type>::allocate(_allocator, 1);
123 std::allocator_traits<allocator_type>::construct(_allocator, new_ptr, *ptr);
124 return std::launder(new_ptr);
125 }
126
133 [[nodiscard]] value_type *copy() const noexcept
134 {
135 auto *new_ptr = std::allocator_traits<allocator_type>::allocate(_allocator, 1);
136 lock();
137 value_type const * const ptr = get();
139 std::allocator_traits<allocator_type>::construct(_allocator, new_ptr, *ptr);
140 unlock();
141 return std::launder(new_ptr);
142 }
143
148 void abort(value_type *ptr) const noexcept
149 {
152 }
153
158 void commit(value_type *ptr) noexcept
159 {
160 lock();
161 auto *old_ptr = exchange(ptr);
162 auto old_version = version();
163 unlock();
164 add_old_copy(old_version, old_ptr);
165 }
166
177 void emplace(auto&&...args) noexcept
178 {
179 value_type *const new_ptr = std::allocator_traits<allocator_type>::allocate(_allocator, 1);
181
182 lock();
183 auto *const old_ptr = exchange(new_ptr);
184 hilet old_version = version();
185 unlock();
186
187 add_old_copy(old_version, old_ptr);
188 }
189
190 [[nodiscard]] bool empty() const noexcept
191 {
192 return not to_bool(_ptr.load(std::memory_order::relaxed));
193 }
194
195 explicit operator bool() const noexcept
196 {
197 return not empty();
198 }
199
200 void reset() noexcept
201 {
202 lock();
203 auto *const old_ptr = _ptr.exchange(nullptr, std::memory_order::release);
204 hilet old_version = *_idle_count;
205 unlock();
206
207 add_old_copy(old_version, old_ptr);
208 }
209
210 rcu& operator=(nullptr_t) noexcept
211 {
212 reset();
213 return *this;
214 }
215
225 void add_old_copy(uint64_t old_version, value_type *old_ptr) noexcept
226 {
227 if (not old_ptr) {
228 return;
229 }
230
231 hilet new_version = version();
232
233 hilet lock = std::scoped_lock(_old_ptrs_mutex);
234 _old_ptrs.emplace_back(old_version, old_ptr);
235
236 // Destroy all objects from previous idle-count versions.
237 auto it = _old_ptrs.begin();
238 while (it != _old_ptrs.end() and it->first < new_version) {
240 std::allocator_traits<allocator_type>::deallocate(_allocator, it->second, 1);
241 ++it;
242 }
243 _old_ptrs.erase(_old_ptrs.begin(), it);
244 }
245
246private:
247 std::atomic<value_type *> _ptr = nullptr;
248 mutable wfree_idle_count _idle_count;
249
250 mutable allocator_type _allocator;
251 mutable unfair_mutex _old_ptrs_mutex;
253};
254
255} // namespace hi::inline v1
256
Definition of the unfair_mutex.
#define hi_assert_not_null(x,...)
Assert if an expression is not nullptr.
Definition assert.hpp:223
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
#define hi_forward(x)
Forward a value, based on the decltype of the value.
Definition utility.hpp:29
DOXYGEN BUG.
Definition algorithm.hpp:13
Read-copy-update.
Definition rcu.hpp:23
constexpr rcu(allocator_type allocator=allocator_type{}) noexcept
Construct a new rcu object.
Definition rcu.hpp:33
void add_old_copy(uint64_t old_version, value_type *old_ptr) noexcept
Add an old copy.
Definition rcu.hpp:225
void unlock() const noexcept
Unlock the rcu pointer for reading.
Definition rcu.hpp:50
void commit(value_type *ptr) noexcept
Commit the copied value.
Definition rcu.hpp:158
value_type * copy() const noexcept
Create a copy of the value.
Definition rcu.hpp:133
value_type const * get() const noexcept
get the rcu-pointer.
Definition rcu.hpp:60
void lock() const noexcept
Lock the rcu pointer for reading.
Definition rcu.hpp:43
value_type * exchange(value_type *ptr) noexcept
Exchange the rcu-pointers.
Definition rcu.hpp:107
value_type const * unsafe_get() noexcept
get the rcu-pointer.
Definition rcu.hpp:72
void emplace(auto &&...args) noexcept
Emplace a new value.
Definition rcu.hpp:177
size_t capacity() const noexcept
Number of objects that are currently allocated.
Definition rcu.hpp:95
uint64_t version() const noexcept
The version of the lock.
Definition rcu.hpp:86
void abort(value_type *ptr) const noexcept
Abort a copy.
Definition rcu.hpp:148
value_type * copy(value_type const *ptr) const noexcept
Create a copy of the value at the given pointer.
Definition rcu.hpp:119
Counts how many times a critical section was idle.
Definition wfree_idle_count.hpp:37
T allocate(T... args)
T construct(T... args)
T deallocate(T... args)
T destroy(T... args)
T lock(T... args)