MT Core (C++)
Core library for replacing C++ standard in project usage
Loading...
Searching...
No Matches
arc.hpp
Go to the documentation of this file.
1/*
2
3Copyright 2025 Matthew Tolman
4
5Licensed under the Apache License, Version 2.0 (the "License");
6you may not use this file except in compliance with the License.
7You may obtain a copy of the License at
8
9 http://www.apache.org/licenses/LICENSE-2.0
10
11Unless required by applicable law or agreed to in writing, software
12distributed under the License is distributed on an "AS IS" BASIS,
13WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14See the License for the specific language governing permissions and
15limitations under the License.
16
17*/
18
19#ifndef MTCORE_MTTHREAD_ARC_HPP
20#define MTCORE_MTTHREAD_ARC_HPP
21
23#include <atomic>
24
25#ifndef MTCORE_NO_STACKTRACE
26#include <cpptrace/basic.hpp>
27#endif
28
35
37namespace mtcore::thread {
38 namespace impl {
39 struct Counter {
40 static constexpr u64 zero_flag = static_cast<u64>(0x1) << static_cast<u64>(63);
41 static constexpr u64 helped_flag = static_cast<u64>(0x1) << static_cast<u64>(62);
42 std::atomic_uint64_t count = 1;
43
44 [[nodiscard]] bool acquire() { return !(count.fetch_add(1) & zero_flag); }
45
46 [[nodiscard]] bool release() {
47 if (count.fetch_sub(1) == 1) {
48 u64 e = 0;
49 if (count.compare_exchange_strong(e, zero_flag)) {
50 return true;
51 }
52 return (e & helped_flag) && count.exchange(zero_flag) & helped_flag;
53 }
54 return false;
55 }
56
57 u64 val() {
58 auto v = count.load();
59 if (v == 0 && count.compare_exchange_strong(v, zero_flag | helped_flag)) {
60 return 0;
61 }
62 return (v & zero_flag) ? 0 : v;
63 }
64 };
65
71 enum class RefError {
72 AllocError = -1,
73 InvalidArgument = -2,
74 };
75
76 template<typename T>
77 struct RefCount {
78 T *value = nullptr;
79 Counter c = {};
80
81 RefCount() = default;
82 RefCount(T *val) : value(val) {}
83 RefCount(RefCount &) = default;
84 RefCount(RefCount &&) noexcept = default;
85 RefCount &operator=(const RefCount &) = default;
86 RefCount &operator=(RefCount &&) noexcept = default;
87
88 template<typename... Args>
89 [[nodiscard]] static Result<RefCount *, AllocationError> make(Allocator &alloc, Args... args) {
90 auto v = alloc.create<T>(args...);
91 if (v.is_error()) {
92 return v.error();
93 }
94
95 auto res = alloc.create<RefCount>(v.value());
96 return res;
97 }
98
99 bool acquire() { return c.acquire(); }
100
101 bool release(Allocator &alloc) {
102 if (c.release()) {
103 alloc.destroy(value);
104 value = nullptr;
105 return true;
106 }
107 return false;
108 }
109 };
110 } // namespace impl
111
116 enum class ArcError {
119 };
120
129 template<typename T>
130 struct Arc;
131
140 template<typename T>
141 struct WeakArc {
142 impl::RefCount<impl::RefCount<T>> *rc = nullptr;
143
150
155 void deinit(Allocator &alloc) {
156 if (!rc) {
157 return;
158 }
159 ensure(rc, "Arc is invalid");
160 ensure(rc->value, "Arc is invalid");
161 if (rc->release(alloc)) {
162 alloc.destroy(rc->value);
163 rc->value = nullptr;
164 alloc.destroy(rc);
165 }
166 rc = nullptr;
167 }
168
174 [[nodiscard]] bool valid() const { return rc != nullptr; }
175
181 ensure(rc, "Arc is invalid");
182 if (rc->acquire()) {
183 return *this;
184 }
186 }
187 };
188
189 template<typename T>
190 struct Arc {
191 impl::RefCount<impl::RefCount<T>> *rc = nullptr;
192
196 [[nodiscard]] T *operator->() {
197 ensure(rc, "Invalid Arc");
198 ensure(rc->value, "Invalid Arc");
199 ensure(rc->value->value, "Invalid Arc");
200 return rc->value->value;
201 }
202
206 [[nodiscard]] const T *operator->() const {
207 ensure(rc, "Invalid Arc");
208 ensure(rc->value, "Invalid Arc");
209 ensure(rc->value->value, "Invalid Arc");
210 return rc->value->value;
211 }
212
216 [[nodiscard]] T &operator*() {
217 ensure(rc, "Invalid Arc");
218 ensure(rc->value, "Invalid Arc");
219 ensure(rc->value->value, "Invalid Arc");
220 return *rc->value->value;
221 }
222
226 [[nodiscard]] const T &operator*() const {
227 ensure(rc, "Invalid Arc");
228 ensure(rc->value, "Invalid Arc");
229 ensure(rc->value->value, "Invalid Arc");
230 return *rc->value->value;
231 }
232
236 [[nodiscard]] bool operator==(const Arc &o) const { return o.rc == rc; }
237
241 [[nodiscard]] bool operator==(T *other) const {
242 if (other == nullptr && rc == nullptr) {
243 return true;
244 }
245 ensure(rc, "Invalid Arc");
246 ensure(rc->value, "Invalid Arc");
247 ensure(rc->value->value, "Invalid Arc");
248 return rc->value->value == other;
249 }
250
254 [[nodiscard]] bool operator==(const T *other) const {
255 if (other == nullptr && rc == nullptr) {
256 return true;
257 }
258 ensure(rc, "Invalid Arc");
259 ensure(rc->value, "Invalid Arc");
260 ensure(rc->value->value, "Invalid Arc");
261 return rc->value->value == other;
262 }
263
267 [[nodiscard]] bool valid() const { return rc != nullptr && rc->value != nullptr && rc->value->value; }
268
272 [[nodiscard]] operator bool() const { return valid(); }
273
281 template<typename... Args>
282 [[nodiscard]] Result<void, AllocationError> init(Allocator &alloc, Args... args) {
283 if (rc) {
284 release(alloc);
285 }
286 Result<T *, AllocationError> valRes = alloc.create<T>(args...);
287 if (valRes.is_error()) {
288 return valRes.error();
289 }
290 T *val = valRes.value();
291
292 auto outerRcRes = impl::RefCount<impl::RefCount<T>>::make(alloc);
293 if (outerRcRes.is_error()) {
294 alloc.destroy(val);
295 return outerRcRes.error();
296 }
297 impl::RefCount<impl::RefCount<T>> *outerRc = outerRcRes.value();
298 outerRc->value->value = valRes.value();
299 rc = outerRc;
300 return success();
301 }
302
309 ensure(rc, "Arc is invalid");
310 if (!rc->acquire()) {
312 }
313 ensure(rc->value, "Arc is invalid");
314 if (!rc->value->acquire()) {
315 rc->release(alloc);
317 }
318 return *this;
319 }
320
325 void release(Allocator &alloc) {
326 if (!rc) {
327 return;
328 }
329 ensure(rc, "Arc is invalid");
330 ensure(rc->value, "Arc is invalid");
331 (void) rc->value->release(alloc);
332 if (rc->release(alloc)) {
333 alloc.destroy(rc->value);
334 rc->value = nullptr;
335 alloc.destroy(rc);
336 }
337 rc = nullptr;
338 }
339
343 void deinit(Allocator &alloc) { release(alloc); }
344
350 ensure(rc, "Invalid Arc");
351 if (!rc->acquire()) {
353 }
354 return success(WeakArc<T>{rc});
355 }
356 };
357
358 template<typename T>
360 ensure(rc, "Arc is invalid");
361 if (!rc->acquire()) {
363 }
364
365 if (!rc->value->acquire()) {
366 rc->release(alloc);
368 }
369 return Arc<T>{rc};
370 }
371} // namespace mtcore::thread
372
373#endif // MTCORE_MTTHREAD_ARC_HPP
ValIter< T > val(const T &r)
Generic value iterator that uses the operator[] and incrementing indexes to iterate over a collection...
Definition iter.hpp:114
ArcError
Error when cannot acquire an ARC.
Definition arc.hpp:116
AllocationError
Error indicating failed allocation.
@ CannotAcquire
Error when a new Arc reference cannot be acquired (e.g.
Definition arc.hpp:118
#define ensure(check,...)
Ensures that a check holds true, aborts the program if not true Will print error if the condition is ...
Success< void > success()
Creates a successful void Result object.
Definition result.hpp:398
Error< Underlying > error(Underlying err)
Creates an error.
Definition result.hpp:425
uint64_t u64
Alias for 64-bit unsigned ints.
Thread-related namespace The methods and classes provided by this class are thread-safe Classes and m...
Represents a memory allocator Exact behavior depends on the underlying VTable used Should use the a_*...
Result< T *, AllocationError > create(Args... args)
Allocates some block of memory with an allocator with a known type Will pass arguments to the constru...
void destroy(T *&ptr)
Destroys an object allocated by this allocator by calling the destructor and freeing memory.
Represents a Result that may have an error (error code) or a success value A type of "void" means the...
Definition result.hpp:170
T value() const noexcept
Checks if is a successful Result (aka.
Definition result.hpp:258
bool is_error() const noexcept
Checks if is an error Result.
Definition result.hpp:266
ErrVal error() const noexcept
Returns the associated error Fails if there is no error;.
Definition result.hpp:294
Automic Reference Count.
Definition arc.hpp:190
bool operator==(T *other) const
Checks if an Arc is equivalent to a pointer (used for nullptr checks)
Definition arc.hpp:241
const T & operator*() const
Dereferences an Arc pointer.
Definition arc.hpp:226
Result< Arc, ArcError > acquire(Allocator &alloc)
Acquires a new Arc reference (increments ref count by 1)
Definition arc.hpp:308
Result< WeakArc< T >, ArcError > weak()
Tries to get a weak pointer.
Definition arc.hpp:349
T * operator->()
Dereferences an Arc pointer.
Definition arc.hpp:196
bool operator==(const T *other) const
Checks if an Arc is equivalent to a pointer (used for nullptr checks)
Definition arc.hpp:254
bool operator==(const Arc &o) const
Checks if an Arc is equivalent to another Arc.
Definition arc.hpp:236
impl::RefCount< impl::RefCount< T > > * rc
Definition arc.hpp:191
bool valid() const
Checks if an Arc is valid (it points to something, and that something still exists)
Definition arc.hpp:267
void deinit(Allocator &alloc)
Definition arc.hpp:343
void release(Allocator &alloc)
Releases an Arc reference (decrements ref count by 1)
Definition arc.hpp:325
Result< void, AllocationError > init(Allocator &alloc, Args... args)
Tries to initialize an ARC to point to a new object.
Definition arc.hpp:282
const T * operator->() const
Dereferences an Arc pointer.
Definition arc.hpp:206
T & operator*()
Dereferences an Arc pointer.
Definition arc.hpp:216
Weak Automic Reference Count.
Definition arc.hpp:141
Result< WeakArc, ArcError > copy()
Tries to copy a weak reference.
Definition arc.hpp:180
Result< Arc< T >, ArcError > obtain(Allocator &alloc)
Tries to obtain a strong reference to what's pointed at.
Definition arc.hpp:359
void deinit(Allocator &alloc)
Deinitializes a weak pointer (may Result in memory cleanup if last weak counter)
Definition arc.hpp:155
impl::RefCount< impl::RefCount< T > > * rc
Definition arc.hpp:142
bool valid() const
Checks if a weak pointer is "valid" as in it's set to point to something Does NOT check if the refere...
Definition arc.hpp:174