react-native-nitro-modules 0.22.0 → 0.22.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cpp/core/ArrayBuffer.cpp +2 -2
- package/cpp/core/ArrayBuffer.hpp +3 -3
- package/cpp/core/HybridObject.hpp +1 -1
- package/cpp/jsi/JSICache.cpp +4 -4
- package/cpp/jsi/JSICache.hpp +18 -18
- package/cpp/jsi/JSIConverter+Function.hpp +3 -2
- package/cpp/prototype/HybridObjectPrototype.cpp +2 -2
- package/cpp/prototype/HybridObjectPrototype.hpp +2 -2
- package/cpp/utils/BorrowingReference.hpp +161 -54
- package/cpp/utils/NitroDefines.hpp +2 -5
- package/cpp/utils/OwningLock.hpp +14 -14
- package/cpp/utils/ReferenceState.hpp +40 -0
- package/cpp/utils/WeakReference+Owning.hpp +33 -0
- package/cpp/utils/WeakReference.hpp +102 -0
- package/cpp/views/CachedProp.hpp +3 -3
- package/ios/core/AnyMapHolder.swift +8 -8
- package/ios/turbomodule/NativeNitroModules+NewArch.mm +1 -1
- package/package.json +1 -1
- package/cpp/utils/BorrowingReference+Owning.hpp +0 -36
- package/cpp/utils/OwningReference.hpp +0 -250
package/cpp/core/ArrayBuffer.cpp
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
//
|
|
7
7
|
|
|
8
8
|
#include "ArrayBuffer.hpp"
|
|
9
|
-
#include "
|
|
9
|
+
#include "BorrowingReference.hpp"
|
|
10
10
|
#include <functional>
|
|
11
11
|
#include <jsi/jsi.h>
|
|
12
12
|
#include <thread>
|
|
@@ -61,7 +61,7 @@ bool NativeArrayBuffer::isOwner() const noexcept {
|
|
|
61
61
|
|
|
62
62
|
// 3. JSArrayBuffer
|
|
63
63
|
|
|
64
|
-
JSArrayBuffer::JSArrayBuffer(jsi::Runtime* runtime,
|
|
64
|
+
JSArrayBuffer::JSArrayBuffer(jsi::Runtime* runtime, BorrowingReference<jsi::ArrayBuffer> jsReference)
|
|
65
65
|
: ArrayBuffer(), _runtime(runtime), _jsReference(jsReference), _initialThreadId(std::this_thread::get_id()) {}
|
|
66
66
|
|
|
67
67
|
JSArrayBuffer::~JSArrayBuffer() {}
|
package/cpp/core/ArrayBuffer.hpp
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
#pragma once
|
|
9
9
|
|
|
10
|
-
#include "
|
|
10
|
+
#include "BorrowingReference.hpp"
|
|
11
11
|
#include <jsi/jsi.h>
|
|
12
12
|
#include <thread>
|
|
13
13
|
#include <vector>
|
|
@@ -122,7 +122,7 @@ private:
|
|
|
122
122
|
*/
|
|
123
123
|
class JSArrayBuffer final : public ArrayBuffer {
|
|
124
124
|
public:
|
|
125
|
-
explicit JSArrayBuffer(jsi::Runtime* runtime,
|
|
125
|
+
explicit JSArrayBuffer(jsi::Runtime* runtime, BorrowingReference<jsi::ArrayBuffer> jsReference);
|
|
126
126
|
~JSArrayBuffer();
|
|
127
127
|
|
|
128
128
|
public:
|
|
@@ -141,7 +141,7 @@ public:
|
|
|
141
141
|
|
|
142
142
|
private:
|
|
143
143
|
jsi::Runtime* _runtime;
|
|
144
|
-
|
|
144
|
+
BorrowingReference<jsi::ArrayBuffer> _jsReference;
|
|
145
145
|
std::thread::id _initialThreadId;
|
|
146
146
|
};
|
|
147
147
|
|
|
@@ -136,7 +136,7 @@ protected:
|
|
|
136
136
|
private:
|
|
137
137
|
static constexpr auto TAG = "HybridObject";
|
|
138
138
|
const char* _name = TAG;
|
|
139
|
-
std::unordered_map<jsi::Runtime*,
|
|
139
|
+
std::unordered_map<jsi::Runtime*, BorrowingReference<jsi::WeakObject>> _objectCache;
|
|
140
140
|
};
|
|
141
141
|
|
|
142
142
|
} // namespace margelo::nitro
|
package/cpp/jsi/JSICache.cpp
CHANGED
|
@@ -14,12 +14,12 @@ namespace margelo::nitro {
|
|
|
14
14
|
static constexpr auto CACHE_PROP_NAME = "__nitroModulesJSICache";
|
|
15
15
|
|
|
16
16
|
template <typename T>
|
|
17
|
-
inline void destroyReferences(const std::vector<
|
|
17
|
+
inline void destroyReferences(const std::vector<WeakReference<T>>& references) {
|
|
18
18
|
for (auto& func : references) {
|
|
19
|
-
|
|
20
|
-
if (
|
|
19
|
+
BorrowingReference<T> reference = func.lock();
|
|
20
|
+
if (reference) {
|
|
21
21
|
// Destroy all functions that we might still have in cache, some callbacks and Promises may now become invalid.
|
|
22
|
-
|
|
22
|
+
reference.destroy();
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
}
|
package/cpp/jsi/JSICache.hpp
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
#include "BorrowingReference.hpp"
|
|
11
11
|
#include "NitroLogger.hpp"
|
|
12
|
-
#include "
|
|
12
|
+
#include "WeakReference.hpp"
|
|
13
13
|
#include <jsi/jsi.h>
|
|
14
14
|
#include <memory>
|
|
15
15
|
#include <mutex>
|
|
@@ -24,12 +24,12 @@ class JSICacheReference;
|
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
26
|
* A `JSICache` can safely store `jsi::Value` instances (e.g. `jsi::Object` or
|
|
27
|
-
* `jsi::Function`) inside `
|
|
27
|
+
* `jsi::Function`) inside `BorrowingReference<T>`.
|
|
28
28
|
*
|
|
29
29
|
* `jsi::Value`s are managed by a `jsi::Runtime`, and will be deleted if the `jsi::Runtime`
|
|
30
30
|
* is deleted - even if there are still strong references to the `jsi::Value`.
|
|
31
31
|
*
|
|
32
|
-
* To access a `
|
|
32
|
+
* To access a `BorrowingReference<jsi::Value>` safely, use `lock()` to get an `OwningLock<jsi::Value>`.
|
|
33
33
|
* This will allow you to access the `jsi::Value` as long as the `OwningLock` is alive,
|
|
34
34
|
* and `JSICache` will hold any garbage collection calls until the `OwningLock` is destroyed.
|
|
35
35
|
*/
|
|
@@ -59,11 +59,11 @@ private:
|
|
|
59
59
|
|
|
60
60
|
private:
|
|
61
61
|
std::mutex _mutex;
|
|
62
|
-
std::vector<
|
|
63
|
-
std::vector<
|
|
64
|
-
std::vector<
|
|
65
|
-
std::vector<
|
|
66
|
-
std::vector<
|
|
62
|
+
std::vector<WeakReference<jsi::Value>> _valueCache;
|
|
63
|
+
std::vector<WeakReference<jsi::Object>> _objectCache;
|
|
64
|
+
std::vector<WeakReference<jsi::Function>> _functionCache;
|
|
65
|
+
std::vector<WeakReference<jsi::WeakObject>> _weakObjectCache;
|
|
66
|
+
std::vector<WeakReference<jsi::ArrayBuffer>> _arrayBufferCache;
|
|
67
67
|
|
|
68
68
|
private:
|
|
69
69
|
static inline std::unordered_map<jsi::Runtime*, std::weak_ptr<JSICache>> _globalCache;
|
|
@@ -83,28 +83,28 @@ public:
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
public:
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
BorrowingReference<jsi::Value> makeShared(jsi::Value&& value) {
|
|
87
|
+
BorrowingReference<jsi::Value> owning(new jsi::Value(std::move(value)));
|
|
88
88
|
_strongCache->_valueCache.push_back(owning.weak());
|
|
89
89
|
return owning;
|
|
90
90
|
}
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
BorrowingReference<jsi::Object> makeShared(jsi::Object&& value) {
|
|
92
|
+
BorrowingReference<jsi::Object> owning(new jsi::Object(std::move(value)));
|
|
93
93
|
_strongCache->_objectCache.push_back(owning.weak());
|
|
94
94
|
return owning;
|
|
95
95
|
}
|
|
96
|
-
|
|
97
|
-
|
|
96
|
+
BorrowingReference<jsi::Function> makeShared(jsi::Function&& value) {
|
|
97
|
+
BorrowingReference<jsi::Function> owning(new jsi::Function(std::move(value)));
|
|
98
98
|
_strongCache->_functionCache.push_back(owning.weak());
|
|
99
99
|
return owning;
|
|
100
100
|
}
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
BorrowingReference<jsi::WeakObject> makeShared(jsi::WeakObject&& value) {
|
|
102
|
+
BorrowingReference<jsi::WeakObject> owning(new jsi::WeakObject(std::move(value)));
|
|
103
103
|
_strongCache->_weakObjectCache.push_back(owning.weak());
|
|
104
104
|
return owning;
|
|
105
105
|
}
|
|
106
|
-
|
|
107
|
-
|
|
106
|
+
BorrowingReference<jsi::ArrayBuffer> makeShared(jsi::ArrayBuffer&& value) {
|
|
107
|
+
BorrowingReference<jsi::ArrayBuffer> owning(new jsi::ArrayBuffer(std::move(value)));
|
|
108
108
|
_strongCache->_arrayBufferCache.push_back(owning.weak());
|
|
109
109
|
return owning;
|
|
110
110
|
}
|
|
@@ -34,7 +34,7 @@ struct JSIConverter<std::function<ReturnType(Args...)>> final {
|
|
|
34
34
|
// Make function global - it'll be managed by the Runtime's memory, and we only have a weak_ref to it.
|
|
35
35
|
auto cache = JSICache::getOrCreateCache(runtime);
|
|
36
36
|
jsi::Function function = arg.asObject(runtime).asFunction(runtime);
|
|
37
|
-
|
|
37
|
+
BorrowingReference<jsi::Function> sharedFunction = cache.makeShared(std::move(function));
|
|
38
38
|
|
|
39
39
|
std::shared_ptr<Dispatcher> strongDispatcher = Dispatcher::getRuntimeGlobalDispatcher(runtime);
|
|
40
40
|
std::weak_ptr<Dispatcher> weakDispatcher = strongDispatcher;
|
|
@@ -94,7 +94,8 @@ struct JSIConverter<std::function<ReturnType(Args...)>> final {
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
private:
|
|
97
|
-
static inline ResultingType callJSFunction(jsi::Runtime& runtime, const
|
|
97
|
+
static inline ResultingType callJSFunction(jsi::Runtime& runtime, const BorrowingReference<jsi::Function>& function,
|
|
98
|
+
const Args&... args) {
|
|
98
99
|
if (!function) {
|
|
99
100
|
if constexpr (std::is_void_v<ResultingType>) {
|
|
100
101
|
// runtime has already been deleted. since this returns void, we can just ignore it being deleted.
|
|
@@ -26,7 +26,7 @@ jsi::Value HybridObjectPrototype::createPrototype(jsi::Runtime& runtime, const s
|
|
|
26
26
|
auto& prototypeCache = _prototypeCache[&runtime];
|
|
27
27
|
auto cachedPrototype = prototypeCache.find(prototype->getNativeInstanceId());
|
|
28
28
|
if (cachedPrototype != prototypeCache.end()) {
|
|
29
|
-
const
|
|
29
|
+
const BorrowingReference<jsi::Object>& cachedObject = cachedPrototype->second;
|
|
30
30
|
return jsi::Value(runtime, *cachedObject).getObject(runtime);
|
|
31
31
|
}
|
|
32
32
|
|
|
@@ -68,7 +68,7 @@ jsi::Value HybridObjectPrototype::createPrototype(jsi::Runtime& runtime, const s
|
|
|
68
68
|
|
|
69
69
|
// 6. Throw it into our cache so the next lookup can be cached and therefore faster
|
|
70
70
|
JSICacheReference jsiCache = JSICache::getOrCreateCache(runtime);
|
|
71
|
-
|
|
71
|
+
BorrowingReference<jsi::Object> cachedObject = jsiCache.makeShared(std::move(object));
|
|
72
72
|
prototypeCache.emplace(prototype->getNativeInstanceId(), cachedObject);
|
|
73
73
|
|
|
74
74
|
// 7. In DEBUG, add a __type info to the prototype object.
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
|
|
8
8
|
#pragma once
|
|
9
9
|
|
|
10
|
+
#include "BorrowingReference.hpp"
|
|
10
11
|
#include "HybridFunction.hpp"
|
|
11
|
-
#include "OwningReference.hpp"
|
|
12
12
|
#include "Prototype.hpp"
|
|
13
13
|
#include "PrototypeChain.hpp"
|
|
14
14
|
#include <functional>
|
|
@@ -46,7 +46,7 @@ public:
|
|
|
46
46
|
|
|
47
47
|
private:
|
|
48
48
|
static jsi::Value createPrototype(jsi::Runtime& runtime, const std::shared_ptr<Prototype>& prototype);
|
|
49
|
-
using PrototypeCache = std::unordered_map<NativeInstanceId,
|
|
49
|
+
using PrototypeCache = std::unordered_map<NativeInstanceId, BorrowingReference<jsi::Object>>;
|
|
50
50
|
static std::unordered_map<jsi::Runtime*, PrototypeCache> _prototypeCache;
|
|
51
51
|
|
|
52
52
|
protected:
|
|
@@ -1,119 +1,226 @@
|
|
|
1
1
|
//
|
|
2
2
|
// BorrowingReference.hpp
|
|
3
|
-
//
|
|
3
|
+
// react-native-nitro
|
|
4
4
|
//
|
|
5
|
-
// Created by Marc Rousavy on
|
|
5
|
+
// Created by Marc Rousavy on 23.06.24.
|
|
6
6
|
//
|
|
7
7
|
|
|
8
8
|
#pragma once
|
|
9
9
|
|
|
10
|
+
#include "NitroDefines.hpp"
|
|
11
|
+
#include "OwningLock.hpp"
|
|
12
|
+
#include "ReferenceState.hpp"
|
|
13
|
+
#include "WeakReference.hpp"
|
|
10
14
|
#include <atomic>
|
|
11
15
|
#include <cstddef>
|
|
12
16
|
#include <mutex>
|
|
13
17
|
|
|
14
18
|
namespace margelo::nitro {
|
|
15
19
|
|
|
16
|
-
// forward-declaration to avoid duplicate symbols
|
|
17
|
-
template <typename T>
|
|
18
|
-
class OwningReference;
|
|
19
|
-
|
|
20
20
|
/**
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
An `BorrowingReference<T>` is a smart-pointer that holds a strong reference to a pointer.
|
|
22
|
+
You can have multiple `BorrowingReference<T>` instances point to the same pointer, as they internally keep a ref-count.
|
|
23
|
+
As opposed to a `shared_ptr<T>`, an `BorrowingReference<T>` can also be imperatively manually deleted, even if there
|
|
24
|
+
are multiple strong references still holding onto the pointer.
|
|
25
|
+
This is useful in cases where the `BorrowingReference` might keep a reference alive, but an external value holder
|
|
26
|
+
is actually responsible for truly deleting the underlying value - like a `jsi::Runtime` for a `jsi::Value`.
|
|
27
|
+
|
|
28
|
+
An `BorrowingReference<T>` can be weakified, which gives the user a `WeakReference<T>`.
|
|
29
|
+
A `WeakReference<T>` can be locked to get an `BorrowingReference<T>` again, assuming it has not been deleted yet.
|
|
30
|
+
|
|
31
|
+
A `BorrowingReference<T>` can also be locked using `lock()`, which gives the user an `OwningLock<T>`.
|
|
32
|
+
Only an `OwningLock<T>` guarantees safe access to the underlying value reference, as any external value holders
|
|
33
|
+
that might try to delete the value will have to wait until the lock is freed again.
|
|
23
34
|
*/
|
|
24
35
|
template <typename T>
|
|
25
36
|
class BorrowingReference final {
|
|
26
|
-
private:
|
|
27
|
-
explicit BorrowingReference(const OwningReference<T>& ref);
|
|
28
|
-
|
|
29
37
|
public:
|
|
30
|
-
BorrowingReference() : _value(nullptr),
|
|
38
|
+
BorrowingReference() : _value(nullptr), _state(nullptr) {}
|
|
31
39
|
|
|
32
|
-
BorrowingReference(
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
if (
|
|
40
|
+
explicit BorrowingReference(T* value) : _value(value), _state(new ReferenceState()) {}
|
|
41
|
+
|
|
42
|
+
BorrowingReference(const BorrowingReference& ref) : _value(ref._value), _state(ref._state) {
|
|
43
|
+
if (_state != nullptr) {
|
|
36
44
|
// increment ref count after copy
|
|
37
|
-
|
|
45
|
+
_state->strongRefCount++;
|
|
38
46
|
}
|
|
39
47
|
}
|
|
40
48
|
|
|
41
|
-
BorrowingReference(BorrowingReference&& ref)
|
|
42
|
-
: _value(ref._value), _isDeleted(ref._isDeleted), _strongRefCount(ref._strongRefCount), _weakRefCount(ref._weakRefCount),
|
|
43
|
-
_mutex(ref._mutex) {
|
|
49
|
+
BorrowingReference(BorrowingReference&& ref) : _value(ref._value), _state(ref._state) {
|
|
44
50
|
ref._value = nullptr;
|
|
45
|
-
ref.
|
|
46
|
-
ref._strongRefCount = nullptr;
|
|
47
|
-
ref._weakRefCount = nullptr;
|
|
51
|
+
ref._state = nullptr;
|
|
48
52
|
}
|
|
49
53
|
|
|
50
54
|
BorrowingReference& operator=(const BorrowingReference& ref) {
|
|
51
55
|
if (this == &ref)
|
|
52
56
|
return *this;
|
|
53
57
|
|
|
54
|
-
if (
|
|
58
|
+
if (_state != nullptr) {
|
|
55
59
|
// destroy previous pointer
|
|
56
|
-
(
|
|
57
|
-
|
|
60
|
+
bool shouldDestroy = _state->decrementStrongRefCount();
|
|
61
|
+
if (shouldDestroy) {
|
|
62
|
+
forceDestroyValue();
|
|
63
|
+
}
|
|
64
|
+
maybeDestroyState();
|
|
58
65
|
}
|
|
59
66
|
|
|
60
67
|
_value = ref._value;
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
_weakRefCount = ref._weakRefCount;
|
|
64
|
-
_mutex = ref._mutex;
|
|
65
|
-
if (_weakRefCount != nullptr) {
|
|
68
|
+
_state = ref._state;
|
|
69
|
+
if (_state != nullptr) {
|
|
66
70
|
// increment new pointer
|
|
67
|
-
|
|
71
|
+
_state->strongRefCount++;
|
|
68
72
|
}
|
|
69
73
|
|
|
70
74
|
return *this;
|
|
71
75
|
}
|
|
72
76
|
|
|
77
|
+
private:
|
|
78
|
+
// WeakReference<T> -> BorrowingReference<T> Lock-constructor
|
|
79
|
+
BorrowingReference(const WeakReference<T>& ref) : _value(ref._value), _state(ref._state) {
|
|
80
|
+
_state->strongRefCount++;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
private:
|
|
84
|
+
// BorrowingReference<C> -> BorrowingReference<T> Cast-constructor
|
|
85
|
+
template <typename OldT>
|
|
86
|
+
BorrowingReference(T* value, const BorrowingReference<OldT>& originalRef) : _value(value), _state(originalRef._state) {
|
|
87
|
+
_state->strongRefCount++;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
template <typename C>
|
|
91
|
+
friend class BorrowingReference;
|
|
92
|
+
|
|
93
|
+
public:
|
|
73
94
|
~BorrowingReference() {
|
|
74
|
-
if (
|
|
95
|
+
if (_state == nullptr) {
|
|
75
96
|
// we are just a dangling nullptr.
|
|
76
97
|
return;
|
|
77
98
|
}
|
|
78
99
|
|
|
79
|
-
|
|
80
|
-
|
|
100
|
+
// decrement strong ref count on destroy
|
|
101
|
+
bool shouldDestroy = _state->decrementStrongRefCount();
|
|
102
|
+
if (shouldDestroy) {
|
|
103
|
+
forceDestroyValue();
|
|
104
|
+
}
|
|
105
|
+
maybeDestroyState();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
public:
|
|
109
|
+
/**
|
|
110
|
+
Casts this `BorrowingReference<T>` to a `BorrowingReference<C>`.
|
|
111
|
+
*/
|
|
112
|
+
template <typename C>
|
|
113
|
+
BorrowingReference<C> as() {
|
|
114
|
+
return BorrowingReference<C>(static_cast<C*>(_value), *this);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
public:
|
|
118
|
+
/**
|
|
119
|
+
Creates an `OwningLock<T>` for the given `BorrowingReference<T>` to guarantee safe
|
|
120
|
+
safe access to `BorrowingReference<T>`.
|
|
121
|
+
Other threads (e.g. the Hermes garbage collector) cannot delete the `BorrowingReference<T>`
|
|
122
|
+
as long as the `OwningLock<T>` is still alive.
|
|
123
|
+
*/
|
|
124
|
+
[[nodiscard]]
|
|
125
|
+
OwningLock<T> lock() const {
|
|
126
|
+
return OwningLock<T>(*this);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
Get whether the `BorrowingReference<T>` is still pointing to a valid value, or not.
|
|
131
|
+
*/
|
|
132
|
+
inline bool hasValue() const {
|
|
133
|
+
return _value != nullptr && !_state->isDeleted;
|
|
81
134
|
}
|
|
82
135
|
|
|
83
136
|
/**
|
|
84
|
-
|
|
137
|
+
Get a borrowing (or "weak") reference to this owning reference
|
|
85
138
|
*/
|
|
86
139
|
[[nodiscard]]
|
|
87
|
-
|
|
140
|
+
WeakReference<T> weak() const {
|
|
141
|
+
return WeakReference(*this);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
Delete and destroy the value this BorrowingReference is pointing to.
|
|
146
|
+
This can even be called if there are still multiple strong references to the value.
|
|
147
|
+
|
|
148
|
+
This will block as long as one or more `OwningLock<T>`s of this `BorrowingReference<T>` are still alive.
|
|
149
|
+
*/
|
|
150
|
+
void destroy() {
|
|
151
|
+
std::unique_lock lock(_state->mutex);
|
|
152
|
+
|
|
153
|
+
forceDestroyValue();
|
|
154
|
+
}
|
|
88
155
|
|
|
89
156
|
public:
|
|
90
|
-
|
|
157
|
+
explicit inline operator bool() const {
|
|
158
|
+
return hasValue();
|
|
159
|
+
}
|
|
91
160
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
161
|
+
inline T& operator*() const {
|
|
162
|
+
#ifdef NITRO_DEBUG
|
|
163
|
+
if (!hasValue()) [[unlikely]] {
|
|
164
|
+
throw std::runtime_error("Tried to dereference (*) nullptr BorrowingReference<T>!");
|
|
165
|
+
}
|
|
166
|
+
#endif
|
|
167
|
+
return *_value;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
inline T* operator->() const {
|
|
171
|
+
#ifdef NITRO_DEBUG
|
|
172
|
+
if (!hasValue()) [[unlikely]] {
|
|
173
|
+
throw std::runtime_error("Tried to dereference (->) nullptr BorrowingReference<T>!");
|
|
174
|
+
}
|
|
175
|
+
#endif
|
|
176
|
+
return _value;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
inline bool operator==(T* other) const {
|
|
180
|
+
return _value == other;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
inline bool operator!=(T* other) const {
|
|
184
|
+
return _value != other;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
inline bool operator==(const BorrowingReference<T>& other) const {
|
|
188
|
+
return _value == other._value;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
inline bool operator!=(const BorrowingReference<T>& other) const {
|
|
192
|
+
return _value != other._value;
|
|
193
|
+
}
|
|
95
194
|
|
|
96
|
-
|
|
195
|
+
private:
|
|
196
|
+
void maybeDestroyState() {
|
|
197
|
+
if (_state->strongRefCount == 0 && _state->weakRefCount == 0) {
|
|
97
198
|
// free the full memory if there are no more references at all
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
delete _isDeleted;
|
|
102
|
-
delete _strongRefCount;
|
|
103
|
-
delete _weakRefCount;
|
|
104
|
-
_mutex->unlock();
|
|
199
|
+
delete _state;
|
|
200
|
+
_state = nullptr;
|
|
105
201
|
return;
|
|
106
202
|
}
|
|
203
|
+
}
|
|
107
204
|
|
|
108
|
-
|
|
205
|
+
void forceDestroyValue() {
|
|
206
|
+
if (_state->isDeleted) [[unlikely]] {
|
|
207
|
+
// it has already been destroyed.
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
delete _value;
|
|
211
|
+
_value = nullptr;
|
|
212
|
+
_state->isDeleted = true;
|
|
109
213
|
}
|
|
110
214
|
|
|
215
|
+
public:
|
|
216
|
+
friend class WeakReference<T>;
|
|
217
|
+
friend class OwningLock<T>;
|
|
218
|
+
|
|
111
219
|
private:
|
|
112
220
|
T* _value;
|
|
113
|
-
|
|
114
|
-
std::atomic_size_t* _strongRefCount;
|
|
115
|
-
std::atomic_size_t* _weakRefCount;
|
|
116
|
-
std::recursive_mutex* _mutex;
|
|
221
|
+
ReferenceState* _state;
|
|
117
222
|
};
|
|
118
223
|
|
|
119
224
|
} // namespace margelo::nitro
|
|
225
|
+
|
|
226
|
+
#include "WeakReference+Owning.hpp"
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
#define NitroDefines_h
|
|
10
10
|
|
|
11
11
|
// Sets the version of the native Nitro core library
|
|
12
|
-
#define NITRO_VERSION "0.22.
|
|
12
|
+
#define NITRO_VERSION "0.22.1"
|
|
13
13
|
|
|
14
14
|
// Sets whether to use debug or optimized production build flags
|
|
15
15
|
#ifdef DEBUG
|
|
@@ -52,10 +52,7 @@
|
|
|
52
52
|
#define SWIFT_NONCOPYABLE
|
|
53
53
|
#endif
|
|
54
54
|
|
|
55
|
-
// React Native Support
|
|
56
|
-
#if __has_include(<cxxreact/ReactNativeVersion.h>)
|
|
57
|
-
#include <cxxreact/ReactNativeVersion.h>
|
|
58
|
-
#endif
|
|
55
|
+
// React Native Support (right now it's only stubbed out)
|
|
59
56
|
#ifndef REACT_NATIVE_VERSION_MINOR
|
|
60
57
|
#define REACT_NATIVE_VERSION_MAJOR 0
|
|
61
58
|
#define REACT_NATIVE_VERSION_MINOR 0
|
package/cpp/utils/OwningLock.hpp
CHANGED
|
@@ -9,10 +9,10 @@
|
|
|
9
9
|
|
|
10
10
|
namespace margelo::nitro {
|
|
11
11
|
template <typename T>
|
|
12
|
-
class
|
|
12
|
+
class BorrowingReference;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
#include "
|
|
15
|
+
#include "BorrowingReference.hpp"
|
|
16
16
|
#include <cstddef>
|
|
17
17
|
#include <mutex>
|
|
18
18
|
|
|
@@ -20,21 +20,26 @@ namespace margelo::nitro {
|
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* An `OwningLock<T>` is a RAII instance that locks the given caller thread guaranteed safe access
|
|
23
|
-
* to a `
|
|
24
|
-
* The `
|
|
23
|
+
* to a `BorrowingReference<T>`.
|
|
24
|
+
* The `BorrowingReference<T>` cannot be deleted while an `OwningLock<T>` of it is alive.
|
|
25
25
|
*
|
|
26
26
|
* This is useful in JSI, because Hermes runs garbage collection on a separate Thread,
|
|
27
|
-
* and the separate Thread can delete an `
|
|
27
|
+
* and the separate Thread can delete an `BorrowingReference<T>` while it's still in use.
|
|
28
28
|
* The `OwningLock<T>` prevents exactly this problem by blocking the GC destructor until
|
|
29
29
|
* the `OwningLock<T>` is released.
|
|
30
30
|
*
|
|
31
|
-
* To create an `OwningLock<T>`, simply call `lock()` on an `
|
|
31
|
+
* To create an `OwningLock<T>`, simply call `lock()` on an `BorrowingReference<T>`.
|
|
32
32
|
*/
|
|
33
33
|
template <typename T>
|
|
34
34
|
class OwningLock final {
|
|
35
|
+
private:
|
|
36
|
+
explicit OwningLock(const BorrowingReference<T>& reference) : _reference(reference) {
|
|
37
|
+
_reference._state->mutex.lock();
|
|
38
|
+
}
|
|
39
|
+
|
|
35
40
|
public:
|
|
36
41
|
~OwningLock() {
|
|
37
|
-
_reference.
|
|
42
|
+
_reference._state->mutex.unlock();
|
|
38
43
|
}
|
|
39
44
|
|
|
40
45
|
OwningLock() = delete;
|
|
@@ -42,15 +47,10 @@ public:
|
|
|
42
47
|
OwningLock(OwningLock&&) = delete;
|
|
43
48
|
|
|
44
49
|
private:
|
|
45
|
-
|
|
46
|
-
_reference._mutex->lock();
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
private:
|
|
50
|
-
OwningReference<T> _reference;
|
|
50
|
+
BorrowingReference<T> _reference;
|
|
51
51
|
|
|
52
52
|
private:
|
|
53
|
-
friend class
|
|
53
|
+
friend class BorrowingReference<T>;
|
|
54
54
|
};
|
|
55
55
|
|
|
56
56
|
} // namespace margelo::nitro
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
//
|
|
2
|
+
// ReferenceState.hpp
|
|
3
|
+
// react-native-nitro
|
|
4
|
+
//
|
|
5
|
+
// Created by Marc Rousavy on 03.02.24.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
#pragma once
|
|
9
|
+
|
|
10
|
+
#include <atomic>
|
|
11
|
+
#include <mutex>
|
|
12
|
+
|
|
13
|
+
namespace margelo::nitro {
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Holds state for an `BorrowingReference` (or `WeakReference`).
|
|
17
|
+
*
|
|
18
|
+
* The state tracks the amount of strong- and weak- references to any kind of value,
|
|
19
|
+
* including an extra `isDeleted` flag that specifies whether the value has been force-deleted.
|
|
20
|
+
*
|
|
21
|
+
* Also, a `mutex` allows for thread-safe access of the `isDeleted` flag.
|
|
22
|
+
*/
|
|
23
|
+
struct ReferenceState {
|
|
24
|
+
std::atomic_size_t strongRefCount;
|
|
25
|
+
std::atomic_size_t weakRefCount;
|
|
26
|
+
bool isDeleted;
|
|
27
|
+
std::mutex mutex;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Decrements the strong ref count by one, and returns whether the value should be deleted.
|
|
31
|
+
*/
|
|
32
|
+
inline bool decrementStrongRefCount() {
|
|
33
|
+
size_t oldRefCount = strongRefCount.fetch_sub(1);
|
|
34
|
+
return oldRefCount <= 1;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
explicit ReferenceState() : strongRefCount(1), weakRefCount(0), isDeleted(false) {}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
} // namespace margelo::nitro
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
//
|
|
2
|
+
// WeakReference+Owning.hpp
|
|
3
|
+
// react-native-nitro
|
|
4
|
+
//
|
|
5
|
+
// Created by Marc Rousavy on 23.06.24.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
#pragma once
|
|
9
|
+
|
|
10
|
+
#include "BorrowingReference.hpp"
|
|
11
|
+
|
|
12
|
+
namespace margelo::nitro {
|
|
13
|
+
|
|
14
|
+
template <typename T>
|
|
15
|
+
WeakReference<T>::WeakReference(const BorrowingReference<T>& ref) {
|
|
16
|
+
_value = ref._value;
|
|
17
|
+
_state = ref._state;
|
|
18
|
+
_state->weakRefCount++;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
template <typename T>
|
|
22
|
+
BorrowingReference<T> WeakReference<T>::lock() const {
|
|
23
|
+
std::unique_lock lock(_state->mutex);
|
|
24
|
+
|
|
25
|
+
if (_state->isDeleted) {
|
|
26
|
+
// return nullptr
|
|
27
|
+
return BorrowingReference<T>();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return BorrowingReference(*this);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
} // namespace margelo::nitro
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
//
|
|
2
|
+
// WeakReference.hpp
|
|
3
|
+
// NitroModules
|
|
4
|
+
//
|
|
5
|
+
// Created by Marc Rousavy on 21.06.24.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
#pragma once
|
|
9
|
+
|
|
10
|
+
#include "ReferenceState.hpp"
|
|
11
|
+
#include <atomic>
|
|
12
|
+
#include <cstddef>
|
|
13
|
+
#include <mutex>
|
|
14
|
+
|
|
15
|
+
namespace margelo::nitro {
|
|
16
|
+
|
|
17
|
+
// forward-declaration to avoid duplicate symbols
|
|
18
|
+
template <typename T>
|
|
19
|
+
class BorrowingReference;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
A `WeakReference<T>` is a weak reference to a pointer created by `BorrowingReference<T>`.
|
|
23
|
+
It can be locked to gain a strong `BorrowingReference<T>` again if it has not been deleted yet.
|
|
24
|
+
*/
|
|
25
|
+
template <typename T>
|
|
26
|
+
class WeakReference final {
|
|
27
|
+
private:
|
|
28
|
+
explicit WeakReference(const BorrowingReference<T>& ref);
|
|
29
|
+
|
|
30
|
+
public:
|
|
31
|
+
WeakReference() : _value(nullptr), _state(nullptr) {}
|
|
32
|
+
|
|
33
|
+
WeakReference(const WeakReference& ref) : _value(ref._value), _state(ref._state) {
|
|
34
|
+
if (_state != nullptr) {
|
|
35
|
+
// increment ref count after copy
|
|
36
|
+
_state->weakRefCount++;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
WeakReference(WeakReference&& ref) : _value(ref._value), _state(ref._state) {
|
|
41
|
+
// Remove state from other WeakReference after moving since it's now stale data
|
|
42
|
+
ref._value = nullptr;
|
|
43
|
+
ref._state = nullptr;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
WeakReference& operator=(const WeakReference& ref) {
|
|
47
|
+
if (this == &ref)
|
|
48
|
+
return *this;
|
|
49
|
+
|
|
50
|
+
if (_state != nullptr) {
|
|
51
|
+
// destroy previous pointer
|
|
52
|
+
_state->weakRefCount--;
|
|
53
|
+
maybeDestroy();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
_value = ref._value;
|
|
57
|
+
_state = ref._state;
|
|
58
|
+
if (_state != nullptr) {
|
|
59
|
+
// increment new pointer
|
|
60
|
+
_state->weakRefCount++;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return *this;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
~WeakReference() {
|
|
67
|
+
if (_state == nullptr) {
|
|
68
|
+
// we are just a dangling nullptr.
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
_state->weakRefCount--;
|
|
73
|
+
maybeDestroy();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
Try to lock the borrowing reference to an owning reference, or `nullptr` if it has already been deleted.
|
|
78
|
+
*/
|
|
79
|
+
[[nodiscard]]
|
|
80
|
+
BorrowingReference<T> lock() const;
|
|
81
|
+
|
|
82
|
+
public:
|
|
83
|
+
friend class BorrowingReference<T>;
|
|
84
|
+
|
|
85
|
+
private:
|
|
86
|
+
void maybeDestroy() {
|
|
87
|
+
if (_state->strongRefCount == 0 && _state->weakRefCount == 0) {
|
|
88
|
+
// free the full memory if there are no more references at all
|
|
89
|
+
if (!_state->isDeleted) [[unlikely]] {
|
|
90
|
+
throw std::runtime_error("WeakReference<T> encountered a stale _value - BorrowingReference<T> should've already deleted this!");
|
|
91
|
+
}
|
|
92
|
+
delete _state;
|
|
93
|
+
_state = nullptr;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
private:
|
|
98
|
+
T* _value;
|
|
99
|
+
ReferenceState* _state;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
} // namespace margelo::nitro
|
package/cpp/views/CachedProp.hpp
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
#pragma once
|
|
6
6
|
|
|
7
|
+
#include "BorrowingReference.hpp"
|
|
7
8
|
#include "JSIConverter.hpp"
|
|
8
9
|
#include "NitroDefines.hpp"
|
|
9
|
-
#include "OwningReference.hpp"
|
|
10
10
|
#include <jsi/jsi.h>
|
|
11
11
|
|
|
12
12
|
namespace margelo::nitro {
|
|
@@ -17,7 +17,7 @@ template <typename T>
|
|
|
17
17
|
struct CachedProp {
|
|
18
18
|
public:
|
|
19
19
|
T value;
|
|
20
|
-
|
|
20
|
+
BorrowingReference<jsi::Value> jsiValue;
|
|
21
21
|
bool isDirty = false;
|
|
22
22
|
|
|
23
23
|
public:
|
|
@@ -35,7 +35,7 @@ public:
|
|
|
35
35
|
}
|
|
36
36
|
T converted = JSIConverter<T>::fromJSI(runtime, value);
|
|
37
37
|
JSICacheReference cache = JSICache::getOrCreateCache(runtime);
|
|
38
|
-
|
|
38
|
+
BorrowingReference<jsi::Value> cached = cache.makeShared(jsi::Value(runtime, value));
|
|
39
39
|
return CachedProp<T>(std::move(converted), std::move(cached), /* isDirty */ true);
|
|
40
40
|
}
|
|
41
41
|
};
|
|
@@ -44,7 +44,7 @@ public indirect enum AnyValue {
|
|
|
44
44
|
/**
|
|
45
45
|
* Represents an `AnyMap` that can be passed to Swift.
|
|
46
46
|
*/
|
|
47
|
-
public class AnyMapHolder {
|
|
47
|
+
public final class AnyMapHolder {
|
|
48
48
|
public let cppPart: margelo.nitro.TSharedMap
|
|
49
49
|
|
|
50
50
|
public init() {
|
|
@@ -54,7 +54,7 @@ public class AnyMapHolder {
|
|
|
54
54
|
public init(withPreallocatedSize size: Int) {
|
|
55
55
|
cppPart = margelo.nitro.AnyMap.make(size)
|
|
56
56
|
}
|
|
57
|
-
|
|
57
|
+
|
|
58
58
|
public init(withCppPart otherCppPart: margelo.nitro.TSharedMap) {
|
|
59
59
|
cppPart = otherCppPart
|
|
60
60
|
}
|
|
@@ -120,7 +120,7 @@ public class AnyMapHolder {
|
|
|
120
120
|
let value = cppPart.pointee.getString(std.string(key))
|
|
121
121
|
return String(value)
|
|
122
122
|
}
|
|
123
|
-
|
|
123
|
+
|
|
124
124
|
/**
|
|
125
125
|
* Gets the array value at the given key.
|
|
126
126
|
* If no value exists at the given key, or if it is not a double,
|
|
@@ -130,7 +130,7 @@ public class AnyMapHolder {
|
|
|
130
130
|
let value = cppPart.pointee.getArray(std.string(key))
|
|
131
131
|
return value.toSwift()
|
|
132
132
|
}
|
|
133
|
-
|
|
133
|
+
|
|
134
134
|
/**
|
|
135
135
|
* Gets the object value at the given key.
|
|
136
136
|
* If no value exists at the given key, or if it is not a double,
|
|
@@ -177,14 +177,14 @@ public class AnyMapHolder {
|
|
|
177
177
|
public func setString(key: String, value: String) {
|
|
178
178
|
cppPart.pointee.setString(std.string(key), std.string(value))
|
|
179
179
|
}
|
|
180
|
-
|
|
180
|
+
|
|
181
181
|
/**
|
|
182
182
|
* Set the given key to the given array value.
|
|
183
183
|
*/
|
|
184
184
|
public func setArray(key: String, value: [AnyValue]) {
|
|
185
185
|
cppPart.pointee.setArray(std.string(key), margelo.nitro.AnyArray.create(value))
|
|
186
186
|
}
|
|
187
|
-
|
|
187
|
+
|
|
188
188
|
/**
|
|
189
189
|
* Set the given key to the given object value.
|
|
190
190
|
*/
|
|
@@ -228,14 +228,14 @@ public class AnyMapHolder {
|
|
|
228
228
|
public func isString(key: String) -> Bool {
|
|
229
229
|
return cppPart.pointee.isString(std.string(key))
|
|
230
230
|
}
|
|
231
|
-
|
|
231
|
+
|
|
232
232
|
/**
|
|
233
233
|
* Gets whether the given `key` is holding an array value, or not.
|
|
234
234
|
*/
|
|
235
235
|
public func isArray(key: String) -> Bool {
|
|
236
236
|
return cppPart.pointee.isArray(std.string(key))
|
|
237
237
|
}
|
|
238
|
-
|
|
238
|
+
|
|
239
239
|
/**
|
|
240
240
|
* Gets whether the given `key` is holding an object value, or not.
|
|
241
241
|
*/
|
|
@@ -34,7 +34,7 @@ RCT_EXPORT_MODULE(NitroModules)
|
|
|
34
34
|
|
|
35
35
|
- (void)installJSIBindingsWithRuntime:(jsi::Runtime&)runtime {
|
|
36
36
|
// 1. Get CallInvoker we cached statically
|
|
37
|
-
|
|
37
|
+
std::shared_ptr<react::CallInvoker> callInvoker = _callInvoker.lock();
|
|
38
38
|
if (callInvoker == nullptr) {
|
|
39
39
|
throw std::runtime_error("Cannot install global.NitroModulesProxy - CallInvoker was null!");
|
|
40
40
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-nitro-modules",
|
|
3
|
-
"version": "0.22.
|
|
3
|
+
"version": "0.22.1",
|
|
4
4
|
"description": "Insanely fast native C++, Swift or Kotlin modules with a statically compiled binding layer to JSI.",
|
|
5
5
|
"main": "lib/commonjs/index",
|
|
6
6
|
"module": "lib/module/index",
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// BorrowingReference+Owning.hpp
|
|
3
|
-
// react-native-nitro
|
|
4
|
-
//
|
|
5
|
-
// Created by Marc Rousavy on 23.06.24.
|
|
6
|
-
//
|
|
7
|
-
|
|
8
|
-
#pragma once
|
|
9
|
-
|
|
10
|
-
#include "OwningReference.hpp"
|
|
11
|
-
|
|
12
|
-
namespace margelo::nitro {
|
|
13
|
-
|
|
14
|
-
template <typename T>
|
|
15
|
-
BorrowingReference<T>::BorrowingReference(const OwningReference<T>& ref) {
|
|
16
|
-
_value = ref._value;
|
|
17
|
-
_isDeleted = ref._isDeleted;
|
|
18
|
-
_strongRefCount = ref._strongRefCount;
|
|
19
|
-
_weakRefCount = ref._weakRefCount;
|
|
20
|
-
_mutex = ref._mutex;
|
|
21
|
-
(*_weakRefCount)++;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
template <typename T>
|
|
25
|
-
OwningReference<T> BorrowingReference<T>::lock() const {
|
|
26
|
-
std::unique_lock lock(*_mutex);
|
|
27
|
-
|
|
28
|
-
if (*_isDeleted) {
|
|
29
|
-
// return nullptr
|
|
30
|
-
return OwningReference<T>();
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return OwningReference(*this);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
} // namespace margelo::nitro
|
|
@@ -1,250 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// OwningReference.hpp
|
|
3
|
-
// react-native-nitro
|
|
4
|
-
//
|
|
5
|
-
// Created by Marc Rousavy on 23.06.24.
|
|
6
|
-
//
|
|
7
|
-
|
|
8
|
-
#pragma once
|
|
9
|
-
|
|
10
|
-
#include "BorrowingReference.hpp"
|
|
11
|
-
#include "NitroDefines.hpp"
|
|
12
|
-
#include "OwningLock.hpp"
|
|
13
|
-
#include <atomic>
|
|
14
|
-
#include <cstddef>
|
|
15
|
-
#include <mutex>
|
|
16
|
-
|
|
17
|
-
namespace margelo::nitro {
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
An `OwningReference<T>` is a smart-pointer that holds a strong reference to a pointer.
|
|
21
|
-
You can have multiple `OwningReference<T>` instances point to the same pointer, as they internally keep a ref-count.
|
|
22
|
-
As opposed to a `shared_ptr<T>`, an `OwningReference<T>` can also be imperatively manually deleted, even if there
|
|
23
|
-
are multiple strong references still holding onto the pointer.
|
|
24
|
-
|
|
25
|
-
An `OwningReference<T>` can be weakified, which gives the user a `BorrowingReference<T>`.
|
|
26
|
-
A `BorrowingReference<T>` can be locked to get an `OwningReference<T>` again, assuming it has not been deleted yet.
|
|
27
|
-
*/
|
|
28
|
-
template <typename T>
|
|
29
|
-
class OwningReference final {
|
|
30
|
-
public:
|
|
31
|
-
using Pointee = T;
|
|
32
|
-
|
|
33
|
-
public:
|
|
34
|
-
OwningReference() : _value(nullptr), _isDeleted(nullptr), _strongRefCount(nullptr), _weakRefCount(nullptr), _mutex(nullptr) {}
|
|
35
|
-
|
|
36
|
-
explicit OwningReference(T* value)
|
|
37
|
-
: _value(value), _isDeleted(new bool(false)), _strongRefCount(new std::atomic_size_t(1)), _weakRefCount(new std::atomic_size_t(0)),
|
|
38
|
-
_mutex(new std::recursive_mutex()) {}
|
|
39
|
-
|
|
40
|
-
OwningReference(const OwningReference& ref)
|
|
41
|
-
: _value(ref._value), _isDeleted(ref._isDeleted), _strongRefCount(ref._strongRefCount), _weakRefCount(ref._weakRefCount),
|
|
42
|
-
_mutex(ref._mutex) {
|
|
43
|
-
if (_strongRefCount != nullptr) {
|
|
44
|
-
// increment ref count after copy
|
|
45
|
-
(*_strongRefCount)++;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
OwningReference(OwningReference&& ref)
|
|
50
|
-
: _value(ref._value), _isDeleted(ref._isDeleted), _strongRefCount(ref._strongRefCount), _weakRefCount(ref._weakRefCount),
|
|
51
|
-
_mutex(ref._mutex) {
|
|
52
|
-
ref._value = nullptr;
|
|
53
|
-
ref._isDeleted = nullptr;
|
|
54
|
-
ref._strongRefCount = nullptr;
|
|
55
|
-
ref._weakRefCount = nullptr;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
OwningReference& operator=(const OwningReference& ref) {
|
|
59
|
-
if (this == &ref)
|
|
60
|
-
return *this;
|
|
61
|
-
|
|
62
|
-
if (_strongRefCount != nullptr) {
|
|
63
|
-
// destroy previous pointer
|
|
64
|
-
(*_strongRefCount)--;
|
|
65
|
-
maybeDestroy();
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
_value = ref._value;
|
|
69
|
-
_isDeleted = ref._isDeleted;
|
|
70
|
-
_strongRefCount = ref._strongRefCount;
|
|
71
|
-
_weakRefCount = ref._weakRefCount;
|
|
72
|
-
_mutex = ref._mutex;
|
|
73
|
-
if (_strongRefCount != nullptr) {
|
|
74
|
-
// increment new pointer
|
|
75
|
-
(*_strongRefCount)++;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
return *this;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
private:
|
|
82
|
-
// BorrowingReference<T> -> OwningReference<T> Lock-constructor
|
|
83
|
-
OwningReference(const BorrowingReference<T>& ref)
|
|
84
|
-
: _value(ref._value), _isDeleted(ref._isDeleted), _strongRefCount(ref._strongRefCount), _weakRefCount(ref._weakRefCount),
|
|
85
|
-
_mutex(ref._mutex) {
|
|
86
|
-
(*_strongRefCount)++;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
private:
|
|
90
|
-
// OwningReference<C> -> OwningReference<T> Cast-constructor
|
|
91
|
-
template <typename OldT>
|
|
92
|
-
OwningReference(T* value, const OwningReference<OldT>& originalRef)
|
|
93
|
-
: _value(value), _isDeleted(originalRef._isDeleted), _strongRefCount(originalRef._strongRefCount),
|
|
94
|
-
_weakRefCount(originalRef._weakRefCount), _mutex(originalRef._mutex) {
|
|
95
|
-
(*_strongRefCount)++;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
template <typename C>
|
|
99
|
-
friend class OwningReference;
|
|
100
|
-
|
|
101
|
-
public:
|
|
102
|
-
~OwningReference() {
|
|
103
|
-
if (_strongRefCount == nullptr) {
|
|
104
|
-
// we are just a dangling nullptr.
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// decrement strong ref count on destroy
|
|
109
|
-
--(*_strongRefCount);
|
|
110
|
-
maybeDestroy();
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
public:
|
|
114
|
-
/**
|
|
115
|
-
Casts this `OwningReference<T>` to a `OwningReference<C>`.
|
|
116
|
-
*/
|
|
117
|
-
template <typename C>
|
|
118
|
-
OwningReference<C> as() {
|
|
119
|
-
return OwningReference<C>(static_cast<C*>(_value), *this);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
public:
|
|
123
|
-
/**
|
|
124
|
-
Creates an `OwningLock<T>` for the given `OwningReference<T>` to guarantee safe
|
|
125
|
-
safe access to `OwningReference<T>`.
|
|
126
|
-
Other threads (e.g. the Hermes garbage collector) cannot delete the `OwningReference<T>`
|
|
127
|
-
as long as the `OwningLock<T>` is still alive.
|
|
128
|
-
*/
|
|
129
|
-
[[nodiscard]]
|
|
130
|
-
OwningLock<T> lock() const {
|
|
131
|
-
return OwningLock<T>(*this);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
Get whether the `OwningReference<T>` is still pointing to a valid value, or not.
|
|
136
|
-
*/
|
|
137
|
-
inline bool hasValue() const {
|
|
138
|
-
return _value != nullptr && !(*_isDeleted);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
Get a borrowing (or "weak") reference to this owning reference
|
|
143
|
-
*/
|
|
144
|
-
[[nodiscard]]
|
|
145
|
-
BorrowingReference<T> weak() const {
|
|
146
|
-
return BorrowingReference(*this);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
Delete and destroy the value this OwningReference is pointing to.
|
|
151
|
-
This can even be called if there are still multiple strong references to the value.
|
|
152
|
-
|
|
153
|
-
This will block as long as one or more `OwningLock<T>`s of this `OwningReference<T>` are still alive.
|
|
154
|
-
*/
|
|
155
|
-
void destroy() {
|
|
156
|
-
std::unique_lock lock(*_mutex);
|
|
157
|
-
|
|
158
|
-
forceDestroy();
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
public:
|
|
162
|
-
explicit inline operator bool() const {
|
|
163
|
-
return hasValue();
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
inline T& operator*() const {
|
|
167
|
-
#ifdef NITRO_DEBUG
|
|
168
|
-
if (!hasValue()) [[unlikely]] {
|
|
169
|
-
throw std::runtime_error("Tried to dereference (*) nullptr OwningReference<T>!");
|
|
170
|
-
}
|
|
171
|
-
#endif
|
|
172
|
-
return *_value;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
inline T* operator->() const {
|
|
176
|
-
#ifdef NITRO_DEBUG
|
|
177
|
-
if (!hasValue()) [[unlikely]] {
|
|
178
|
-
throw std::runtime_error("Tried to dereference (->) nullptr OwningReference<T>!");
|
|
179
|
-
}
|
|
180
|
-
#endif
|
|
181
|
-
return _value;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
inline bool operator==(T* other) const {
|
|
185
|
-
std::unique_lock lock(*_mutex);
|
|
186
|
-
|
|
187
|
-
if (*_isDeleted) {
|
|
188
|
-
return other == nullptr;
|
|
189
|
-
} else {
|
|
190
|
-
return other == _value;
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
inline bool operator!=(T* other) const {
|
|
195
|
-
return !(this == other);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
inline bool operator==(const OwningReference<T>& other) const {
|
|
199
|
-
return _value == other._value;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
inline bool operator!=(const OwningReference<T>& other) const {
|
|
203
|
-
return !(this == other);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
private:
|
|
207
|
-
void maybeDestroy() {
|
|
208
|
-
_mutex->lock();
|
|
209
|
-
|
|
210
|
-
if (*_strongRefCount == 0) {
|
|
211
|
-
// after no strong references exist anymore
|
|
212
|
-
forceDestroy();
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
if (*_strongRefCount == 0 && *_weakRefCount == 0) {
|
|
216
|
-
// free the full memory if there are no more references at all
|
|
217
|
-
delete _isDeleted;
|
|
218
|
-
delete _strongRefCount;
|
|
219
|
-
delete _weakRefCount;
|
|
220
|
-
_mutex->unlock();
|
|
221
|
-
return;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
_mutex->unlock();
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
void forceDestroy() {
|
|
228
|
-
if (*_isDeleted) {
|
|
229
|
-
// it has already been destroyed.
|
|
230
|
-
return;
|
|
231
|
-
}
|
|
232
|
-
delete _value;
|
|
233
|
-
*_isDeleted = true;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
public:
|
|
237
|
-
friend class BorrowingReference<T>;
|
|
238
|
-
friend class OwningLock<T>;
|
|
239
|
-
|
|
240
|
-
private:
|
|
241
|
-
T* _value;
|
|
242
|
-
bool* _isDeleted;
|
|
243
|
-
std::atomic_size_t* _strongRefCount;
|
|
244
|
-
std::atomic_size_t* _weakRefCount;
|
|
245
|
-
std::recursive_mutex* _mutex;
|
|
246
|
-
};
|
|
247
|
-
|
|
248
|
-
} // namespace margelo::nitro
|
|
249
|
-
|
|
250
|
-
#include "BorrowingReference+Owning.hpp"
|