MT Core (C++)
Core library for replacing C++ standard in project usage
Loading...
Searching...
No Matches
rc.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_RC_HPP
20#define MTCORE_RC_HPP
21
22#include "mtcore/alloc.hpp"
24#include "mtcore/core.hpp"
25
26namespace mtcore {
27 namespace impl {
28 struct RcCounter {
29 u64 count = 1;
30
31 bool acquire() {
32 if (count == 0) {
33 return false;
34 }
35 ++count;
36 return true;
37 }
38
39 bool release() {
40 ensure(count > 0, "DOUBLE RELEASE DETECTED!");
41 --count;
42 return count == 0;
43 }
44
45 u64 val() { return count; }
46 };
47
48 enum class StrongReleaseAction {
49 NONE,
50 CLEANUP_RC,
51 CLEANUP_VAR_ONLY,
52 };
53
54 template<typename T>
55 struct ThreadLocalRefCount {
56 T *value = nullptr;
57 u64 strongCounter = 1;
58 u64 weakCounter = 0;
59
60 bool acquireStrong() {
61 if (strongCounter == 0 || value == nullptr) {
62 return false;
63 }
64 ++strongCounter;
65 return true;
66 }
67
68 bool acquireWeak() {
69 if (strongCounter == 0 && weakCounter == 0) {
70 return false;
71 }
72 ++weakCounter;
73 return true;
74 }
75
76 StrongReleaseAction releaseStrong() {
77 ensure(strongCounter > 0, "Double release detected");
78 --strongCounter;
79 if (strongCounter > 0) {
80 return StrongReleaseAction::NONE;
81 }
82 else if (weakCounter == 0) {
83 return StrongReleaseAction::CLEANUP_RC;
84 }
85 else {
86 return StrongReleaseAction::CLEANUP_VAR_ONLY;
87 }
88 }
89
90 bool releaseWeak() {
91 ensure(weakCounter > 0, "Double release detected");
92 --weakCounter;
93 return weakCounter == 0 && strongCounter == 0;
94 }
95 };
96 } // namespace impl
97
101 enum class RcError {
103 };
104
113 template<typename T>
114 struct Rc;
115
123 template<typename T>
124 struct WeakRc {
125 impl::ThreadLocalRefCount<T> *rc = nullptr;
126
133
139 void deinit(Allocator &alloc) {
140 if (!rc) {
141 return;
142 }
143
144 ensure(rc, "Rc is invalid");
145 if (rc->releaseWeak()) {
146 alloc.destroy(rc);
147 }
148 }
149
153 [[nodiscard]] bool valid() const { return rc != nullptr; }
154
158 [[nodiscard]] operator bool() const { return valid(); }
159
164 ensure(rc, "Rc is invalid");
165 if (!rc->acquireWeak()) {
167 }
168 return *this;
169 }
170 };
171
172 template<typename T>
173 struct Rc {
174 impl::ThreadLocalRefCount<T> *rc = nullptr;
175
177 [[nodiscard]] T &operator*() {
178 ensure(rc, "Invalid Rc");
179 return *rc->value;
180 }
181
183 [[nodiscard]] const T &operator*() const {
184 ensure(rc, "Invalid Rc");
185 return *rc->value;
186 }
187
189 [[nodiscard]] T *operator->() {
190 ensure(rc, "Invalid Rc");
191 return rc->value;
192 }
193
195 [[nodiscard]] const T *operator->() const {
196 ensure(rc, "Invalid Rc");
197 return rc->value;
198 }
199
201 [[nodiscard]] bool operator==(const Rc &other) const { return rc == other.rc; }
202
204 [[nodiscard]] bool operator==(T *other) const {
205 if (other == nullptr && rc == nullptr) {
206 return true;
207 }
208 ensure(rc, "Invalid Rc");
209 return rc->value == other;
210 }
211
213 [[nodiscard]] bool operator==(const T *other) const {
214 if (other == nullptr && rc == nullptr) {
215 return true;
216 }
217 ensure(rc, "Invalid Rc");
218 return rc->value == other;
219 }
220
222 [[nodiscard]] bool valid() const { return rc != nullptr && rc->value != nullptr; }
223
225 [[nodiscard]] operator bool() const { return valid(); }
226
234 template<typename... Args>
235 [[nodiscard]] Result<void, AllocationError> init(Allocator &alloc, Args... args) {
236 if (rc) {
237 release(alloc);
238 }
239 Result<T *, AllocationError> valRes = alloc.create<T>(args...);
240 if (valRes.is_error()) {
241 return valRes.error();
242 }
243 T *val = valRes.value();
244
245 auto rcRes = alloc.create<impl::ThreadLocalRefCount<T>>();
246 if (rcRes.is_error()) {
247 alloc.destroy(val);
249 }
250 rc = rcRes.value();
251 rc->value = val;
252 return success();
253 }
254
259 [[nodiscard]] Result<Rc, RcError> acquire() {
260 ensure(rc, "Rc is invalid");
261 if (!rc->acquireStrong()) {
263 }
264 ensure(rc->value, "Rc is invalid");
265 return *this;
266 }
267
272 void release(Allocator &alloc) {
273 if (!rc) {
274 return;
275 }
276 ensure(rc, "Rc is invalid");
277 ensure(rc->value, "Rc is invalid");
278 switch (impl::StrongReleaseAction action = rc->releaseStrong()) {
279 case impl::StrongReleaseAction::NONE:
280 break;
281 case impl::StrongReleaseAction::CLEANUP_RC:
282 alloc.destroy(rc->value);
283 rc->value = nullptr;
284 alloc.destroy(rc);
285 break;
286 case impl::StrongReleaseAction::CLEANUP_VAR_ONLY:
287 alloc.destroy(rc->value);
288 rc->value = nullptr;
289 break;
290 }
291 rc = nullptr;
292 }
293
295 void deinit(Allocator &alloc) { release(alloc); }
296
301 ensure(rc, "Invalid Rc");
302 if (!rc->acquireWeak()) {
304 }
305 return success(WeakRc<T>{rc});
306 }
307 };
308
309 template<typename T>
311 ensure(rc, "arc is invalid");
312 if (!rc->acquireStrong()) {
314 }
315 return Rc<T>{rc};
316 }
317
318} // namespace mtcore
319
320#endif // MTCORE_RC_HPP
#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.
Core library for C++ with Zig-related functionality.
RcError
Error for thread-local reference counting.
Definition rc.hpp:101
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.
Thread-local reference counted pointer Not thread safe, don't share Reference counting for cleaning u...
Definition rc.hpp:173
void deinit(Allocator &alloc)
Same as release.
Definition rc.hpp:295
T * operator->()
Dereferences pointer.
Definition rc.hpp:189
bool operator==(const T *other) const
Compares to a pointer.
Definition rc.hpp:213
Result< WeakRc< T >, RcError > weak()
Attempts to get a weak reference.
Definition rc.hpp:300
bool operator==(T *other) const
Compares to a pointer.
Definition rc.hpp:204
T & operator*()
Dereferences pointer.
Definition rc.hpp:177
Result< void, AllocationError > init(Allocator &alloc, Args... args)
Initializes a reference counter to point to a new entity.
Definition rc.hpp:235
const T & operator*() const
Dereferences pointer.
Definition rc.hpp:183
const T * operator->() const
Dereferences pointer.
Definition rc.hpp:195
bool operator==(const Rc &other) const
Compares to a pointer.
Definition rc.hpp:201
void release(Allocator &alloc)
Releases a reference.
Definition rc.hpp:272
bool valid() const
Checks if is valid.
Definition rc.hpp:222
impl::ThreadLocalRefCount< T > * rc
Definition rc.hpp:174
Result< Rc, RcError > acquire()
Tries to acquire another strong reference Will return either another strong reference or an error.
Definition rc.hpp:259
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
Thread-local weak reference counted pointer Not thread safe, don't share Does not hold lifetime.
Definition rc.hpp:124
Result< WeakRc, RcError > copy()
Tries to copy a weak reference.
Definition rc.hpp:163
Result< Rc< T >, RcError > obtain(Allocator &alloc)
Tries to obtain a strong reference May error if could not obtain (e.g.
Definition rc.hpp:310
void deinit(Allocator &alloc)
Deinitializes a weak pointer Will decrement weak count and may clean up counter.
Definition rc.hpp:139
impl::ThreadLocalRefCount< T > * rc
Definition rc.hpp:125
bool valid() const
Checks if weak reference is valid.
Definition rc.hpp:153