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.
@@ -6,7 +6,7 @@
6
6
  //
7
7
 
8
8
  #include "ArrayBuffer.hpp"
9
- #include "OwningReference.hpp"
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, OwningReference<jsi::ArrayBuffer> jsReference)
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() {}
@@ -7,7 +7,7 @@
7
7
 
8
8
  #pragma once
9
9
 
10
- #include "OwningReference.hpp"
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, OwningReference<jsi::ArrayBuffer> jsReference);
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
- OwningReference<jsi::ArrayBuffer> _jsReference;
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*, OwningReference<jsi::WeakObject>> _objectCache;
139
+ std::unordered_map<jsi::Runtime*, BorrowingReference<jsi::WeakObject>> _objectCache;
140
140
  };
141
141
 
142
142
  } // namespace margelo::nitro
@@ -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<BorrowingReference<T>>& references) {
17
+ inline void destroyReferences(const std::vector<WeakReference<T>>& references) {
18
18
  for (auto& func : references) {
19
- OwningReference<T> owning = func.lock();
20
- if (owning) {
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
- owning.destroy();
22
+ reference.destroy();
23
23
  }
24
24
  }
25
25
  }
@@ -9,7 +9,7 @@
9
9
 
10
10
  #include "BorrowingReference.hpp"
11
11
  #include "NitroLogger.hpp"
12
- #include "OwningReference.hpp"
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 `OwningReference<T>`.
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 `OwningReference<jsi::Value>` safely, use `lock()` to get an `OwningLock<jsi::Value>`.
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<BorrowingReference<jsi::Value>> _valueCache;
63
- std::vector<BorrowingReference<jsi::Object>> _objectCache;
64
- std::vector<BorrowingReference<jsi::Function>> _functionCache;
65
- std::vector<BorrowingReference<jsi::WeakObject>> _weakObjectCache;
66
- std::vector<BorrowingReference<jsi::ArrayBuffer>> _arrayBufferCache;
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
- OwningReference<jsi::Value> makeShared(jsi::Value&& value) {
87
- OwningReference<jsi::Value> owning(new jsi::Value(std::move(value)));
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
- OwningReference<jsi::Object> makeShared(jsi::Object&& value) {
92
- OwningReference<jsi::Object> owning(new jsi::Object(std::move(value)));
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
- OwningReference<jsi::Function> makeShared(jsi::Function&& value) {
97
- OwningReference<jsi::Function> owning(new jsi::Function(std::move(value)));
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
- OwningReference<jsi::WeakObject> makeShared(jsi::WeakObject&& value) {
102
- OwningReference<jsi::WeakObject> owning(new jsi::WeakObject(std::move(value)));
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
- OwningReference<jsi::ArrayBuffer> makeShared(jsi::ArrayBuffer&& value) {
107
- OwningReference<jsi::ArrayBuffer> owning(new jsi::ArrayBuffer(std::move(value)));
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
- OwningReference<jsi::Function> sharedFunction = cache.makeShared(std::move(function));
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 OwningReference<jsi::Function>& function, const Args&... args) {
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 OwningReference<jsi::Object>& cachedObject = cachedPrototype->second;
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
- OwningReference<jsi::Object> cachedObject = jsiCache.makeShared(std::move(object));
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, OwningReference<jsi::Object>>;
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
- // NitroModules
3
+ // react-native-nitro
4
4
  //
5
- // Created by Marc Rousavy on 21.06.24.
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
- A `BorrowingReference<T>` is a weak reference to a pointer created by `OwningReference<T>`.
22
- It can be locked to gain a strong `OwningReference<T>` again if it has not been deleted yet.
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), _isDeleted(nullptr), _strongRefCount(nullptr), _weakRefCount(nullptr), _mutex(nullptr) {}
38
+ BorrowingReference() : _value(nullptr), _state(nullptr) {}
31
39
 
32
- BorrowingReference(const BorrowingReference& ref)
33
- : _value(ref._value), _isDeleted(ref._isDeleted), _strongRefCount(ref._strongRefCount), _weakRefCount(ref._weakRefCount),
34
- _mutex(ref._mutex) {
35
- if (_weakRefCount != nullptr) {
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
- (*_weakRefCount)++;
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._isDeleted = nullptr;
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 (_weakRefCount != nullptr) {
58
+ if (_state != nullptr) {
55
59
  // destroy previous pointer
56
- (*_weakRefCount)--;
57
- maybeDestroy();
60
+ bool shouldDestroy = _state->decrementStrongRefCount();
61
+ if (shouldDestroy) {
62
+ forceDestroyValue();
63
+ }
64
+ maybeDestroyState();
58
65
  }
59
66
 
60
67
  _value = ref._value;
61
- _isDeleted = ref._isDeleted;
62
- _strongRefCount = ref._strongRefCount;
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
- (*_weakRefCount)++;
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 (_weakRefCount == nullptr) {
95
+ if (_state == nullptr) {
75
96
  // we are just a dangling nullptr.
76
97
  return;
77
98
  }
78
99
 
79
- (*_weakRefCount)--;
80
- maybeDestroy();
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
- Try to lock the borrowing reference to an owning reference, or `nullptr` if it has already been deleted.
137
+ Get a borrowing (or "weak") reference to this owning reference
85
138
  */
86
139
  [[nodiscard]]
87
- OwningReference<T> lock() const;
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
- friend class OwningReference<T>;
157
+ explicit inline operator bool() const {
158
+ return hasValue();
159
+ }
91
160
 
92
- private:
93
- void maybeDestroy() {
94
- _mutex->lock();
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
- if (*_strongRefCount == 0 && *_weakRefCount == 0) {
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
- if (!(*_isDeleted)) {
99
- delete _value;
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
- _mutex->unlock();
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
- bool* _isDeleted;
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.0"
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
@@ -9,10 +9,10 @@
9
9
 
10
10
  namespace margelo::nitro {
11
11
  template <typename T>
12
- class OwningReference;
12
+ class BorrowingReference;
13
13
  }
14
14
 
15
- #include "OwningReference.hpp"
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 `OwningReference<T>`.
24
- * The `OwningReference<T>` cannot be deleted while an `OwningLock<T>` of it is alive.
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 `OwningReference<T>` while it's still in use.
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 `OwningReference<T>`.
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._mutex->unlock();
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
- explicit OwningLock(const OwningReference<T>& reference) : _reference(reference) {
46
- _reference._mutex->lock();
47
- }
48
-
49
- private:
50
- OwningReference<T> _reference;
50
+ BorrowingReference<T> _reference;
51
51
 
52
52
  private:
53
- friend class OwningReference<T>;
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
@@ -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
- OwningReference<jsi::Value> jsiValue;
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
- OwningReference<jsi::Value> cached = cache.makeShared(jsi::Value(runtime, value));
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
- auto callInvoker = _callInvoker.lock();
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.0",
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"