react-native-nitro-modules 0.21.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.
Files changed (76) hide show
  1. package/NitroModules.podspec +4 -1
  2. package/README.md +1 -1
  3. package/android/CMakeLists.txt +1 -0
  4. package/android/build.gradle +4 -2
  5. package/android/gradle.properties +2 -2
  6. package/android/src/main/cpp/platform/ThreadUtils.cpp +4 -4
  7. package/android/src/main/cpp/registry/JHybridObjectRegistry.hpp +2 -0
  8. package/android/src/main/java/com/margelo/nitro/core/HybridObject.kt +10 -9
  9. package/android/src/main/java/com/margelo/nitro/core/HybridObjectInitializer.java +4 -0
  10. package/android/src/main/java/com/margelo/nitro/core/HybridObjectRegistry.java +2 -0
  11. package/android/src/main/java/com/margelo/nitro/views/HybridView.kt +23 -0
  12. package/cpp/core/ArrayBuffer.cpp +2 -2
  13. package/cpp/core/ArrayBuffer.hpp +3 -3
  14. package/cpp/core/HybridFunction.hpp +1 -1
  15. package/cpp/core/HybridObject.cpp +7 -6
  16. package/cpp/core/HybridObject.hpp +3 -3
  17. package/cpp/core/Promise.hpp +1 -1
  18. package/cpp/entrypoint/HybridNitroModulesProxy.cpp +8 -0
  19. package/cpp/entrypoint/HybridNitroModulesProxy.hpp +1 -0
  20. package/cpp/jsi/JSICache.cpp +5 -4
  21. package/cpp/jsi/JSICache.hpp +21 -15
  22. package/cpp/jsi/JSIConverter+Exception.hpp +1 -1
  23. package/cpp/jsi/JSIConverter+Function.hpp +3 -2
  24. package/cpp/jsi/JSIConverter+HostObject.hpp +1 -1
  25. package/cpp/jsi/JSIConverter+HybridObject.hpp +1 -1
  26. package/cpp/jsi/JSIConverter+Tuple.hpp +1 -1
  27. package/cpp/jsi/JSIConverter+Variant.hpp +1 -1
  28. package/cpp/jsi/JSIHelpers.hpp +1 -1
  29. package/cpp/prototype/HybridObjectPrototype.cpp +3 -3
  30. package/cpp/prototype/HybridObjectPrototype.hpp +2 -2
  31. package/cpp/registry/HybridObjectRegistry.cpp +25 -12
  32. package/cpp/registry/HybridObjectRegistry.hpp +1 -0
  33. package/cpp/utils/AssertPromiseState.hpp +1 -1
  34. package/cpp/utils/BorrowingReference.hpp +163 -54
  35. package/cpp/utils/NitroDefines.hpp +10 -1
  36. package/cpp/utils/NitroHash.hpp +17 -0
  37. package/cpp/utils/{TypeInfo.hpp → NitroTypeInfo.hpp} +1 -1
  38. package/cpp/utils/OwningLock.hpp +14 -14
  39. package/cpp/utils/ReferenceState.hpp +40 -0
  40. package/cpp/utils/WeakReference+Owning.hpp +33 -0
  41. package/cpp/utils/WeakReference.hpp +102 -0
  42. package/cpp/views/CachedProp.hpp +43 -0
  43. package/ios/core/AnyMapHolder.swift +8 -8
  44. package/ios/core/HybridContext.hpp +1 -1
  45. package/ios/core/{HybridObjectSpec.swift → HybridObject.swift} +10 -2
  46. package/ios/turbomodule/NativeNitroModules+NewArch.mm +1 -1
  47. package/ios/utils/RuntimeError.hpp +1 -1
  48. package/ios/views/HybridView.swift +41 -0
  49. package/lib/commonjs/index.js +11 -0
  50. package/lib/commonjs/index.js.map +1 -1
  51. package/lib/commonjs/views/HybridView.js +17 -0
  52. package/lib/commonjs/views/HybridView.js.map +1 -0
  53. package/lib/commonjs/views/getHostComponent.js +22 -0
  54. package/lib/commonjs/views/getHostComponent.js.map +1 -0
  55. package/lib/module/index.js +1 -0
  56. package/lib/module/index.js.map +1 -1
  57. package/lib/module/views/HybridView.js +30 -0
  58. package/lib/module/views/HybridView.js.map +1 -0
  59. package/lib/module/views/getHostComponent.js +15 -0
  60. package/lib/module/views/getHostComponent.js.map +1 -0
  61. package/lib/tsconfig.build.tsbuildinfo +1 -1
  62. package/lib/typescript/NitroModulesProxy.d.ts +7 -0
  63. package/lib/typescript/NitroModulesProxy.d.ts.map +1 -1
  64. package/lib/typescript/index.d.ts +1 -0
  65. package/lib/typescript/index.d.ts.map +1 -1
  66. package/lib/typescript/views/HybridView.d.ts +35 -0
  67. package/lib/typescript/views/HybridView.d.ts.map +1 -0
  68. package/lib/typescript/views/getHostComponent.d.ts +13 -0
  69. package/lib/typescript/views/getHostComponent.d.ts.map +1 -0
  70. package/package.json +3 -2
  71. package/src/NitroModulesProxy.ts +8 -0
  72. package/src/index.ts +1 -0
  73. package/src/views/HybridView.ts +37 -0
  74. package/src/views/getHostComponent.ts +26 -0
  75. package/cpp/utils/BorrowingReference+Owning.hpp +0 -36
  76. package/cpp/utils/OwningReference.hpp +0 -237
@@ -8,6 +8,7 @@
8
8
  #include "HybridObjectRegistry.hpp"
9
9
  #include "NitroDefines.hpp"
10
10
  #include "NitroLogger.hpp"
11
+ #include <numeric>
11
12
 
12
13
  namespace margelo::nitro {
13
14
 
@@ -29,17 +30,25 @@ std::vector<std::string> HybridObjectRegistry::getAllHybridObjectNames() {
29
30
  return keys;
30
31
  }
31
32
 
33
+ std::string HybridObjectRegistry::getAllRegisteredHybridObjectNamesToString() {
34
+ std::vector<std::string> names = getAllHybridObjectNames();
35
+ return std::accumulate(std::next(names.begin()), names.end(), names[0], [](std::string a, std::string b) { return a + ", " + b; });
36
+ }
37
+
32
38
  void HybridObjectRegistry::registerHybridObjectConstructor(const std::string& hybridObjectName, HybridObjectConstructorFn&& constructorFn) {
33
39
  Logger::log(LogLevel::Info, TAG, "Registering HybridObject \"%s\"...", hybridObjectName.c_str());
34
40
  auto& map = HybridObjectRegistry::getRegistry();
35
41
  #ifdef NITRO_DEBUG
36
42
  if (map.contains(hybridObjectName)) [[unlikely]] {
43
+ auto allObjectNames = getAllRegisteredHybridObjectNamesToString();
37
44
  auto message =
38
- "HybridObject \"" + std::string(hybridObjectName) +
45
+ "HybridObject \"" + hybridObjectName +
39
46
  "\" has already been "
40
47
  "registered in the Nitro Modules HybridObjectRegistry! Suggestions:\n"
41
48
  "- If you just installed another library, maybe both libraries are using the same name?\n"
42
- "- If you just registered your own HybridObject, maybe you accidentally called `registerHybridObjectConstructor(...)` twice?";
49
+ "- If you just registered your own HybridObject, maybe you accidentally called `registerHybridObjectConstructor(...)` twice?\n"
50
+ "- All registered HybridObjects: [" +
51
+ allObjectNames + "]";
43
52
  throw std::runtime_error(message);
44
53
  }
45
54
  #endif
@@ -57,16 +66,20 @@ std::shared_ptr<HybridObject> HybridObjectRegistry::createHybridObject(const std
57
66
  auto& map = HybridObjectRegistry::getRegistry();
58
67
  auto fn = map.find(hybridObjectName);
59
68
  if (fn == map.end()) [[unlikely]] {
60
- auto message = "Cannot create an instance of HybridObject \"" + std::string(hybridObjectName) +
61
- "\" - It has not yet been registered in the Nitro Modules HybridObjectRegistry! Suggestions:\n"
62
- "- If you use Nitrogen, make sure your `nitro.json` contains `" +
63
- std::string(hybridObjectName) +
64
- "` on this platform.\n"
65
- "- If you use Nitrogen, make sure your library (*Package.java)/app (MainApplication.java) calls "
66
- "`System.loadLibrary(\"$$androidCxxLibName$$\")` somewhere on app-startup.\n"
67
- "- If you use Nitrogen, make sure your cpp-adapter.cpp calls `margelo::nitro::$$cxxNamespace$$::initialize(vm)`.\n"
68
- "- If you use Nitrogen, inspect the generated `$$androidCxxLibName$$OnLoad.cpp` file.\n"
69
- "- If you don't use Nitrogen, make sure you called `HybridObjectRegistry.registerHybridObject(...)`.";
69
+ auto allObjectNames = getAllRegisteredHybridObjectNamesToString();
70
+ auto message =
71
+ "Cannot create an instance of HybridObject \"" + hybridObjectName +
72
+ "\" - It has not yet been registered in the Nitro Modules HybridObjectRegistry! Suggestions:\n"
73
+ "- If you use Nitrogen, make sure your `nitro.json` contains `" +
74
+ hybridObjectName +
75
+ "` on this platform.\n"
76
+ "- If you use Nitrogen, make sure your library (*Package.java)/app (MainApplication.java) calls "
77
+ "`$$androidCxxLibName$$OnLoad.initializeNative()` somewhere on app-startup.\n"
78
+ "- If you use Nitrogen, make sure your `cpp-adapter.cpp`/`OnLoad.cpp` calls `margelo::nitro::$$cxxNamespace$$::initialize(vm)`.\n"
79
+ "- If you use Nitrogen, inspect the generated `$$androidCxxLibName$$OnLoad.cpp` file.\n"
80
+ "- If you don't use Nitrogen, make sure you called `HybridObjectRegistry.registerHybridObject(...)`."
81
+ "- All registered HybridObjects: [" +
82
+ allObjectNames + "]";
70
83
  throw std::runtime_error(message);
71
84
  }
72
85
  std::shared_ptr<HybridObject> instance = fn->second();
@@ -44,6 +44,7 @@ public:
44
44
 
45
45
  private:
46
46
  static std::unordered_map<std::string, HybridObjectConstructorFn>& getRegistry();
47
+ static std::string getAllRegisteredHybridObjectNamesToString();
47
48
 
48
49
  private:
49
50
  static constexpr auto TAG = "HybridObjectRegistry";
@@ -12,8 +12,8 @@ template <typename TResult>
12
12
  class Promise;
13
13
  } // namespace margelo::nitro
14
14
 
15
+ #include "NitroTypeInfo.hpp"
15
16
  #include "Promise.hpp"
16
- #include "TypeInfo.hpp"
17
17
  #include <exception>
18
18
  #include <string>
19
19
 
@@ -1,117 +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
- // increment ref count after copy
36
- (*_weakRefCount)++;
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) {
44
+ // increment ref count after copy
45
+ _state->strongRefCount++;
46
+ }
37
47
  }
38
48
 
39
- BorrowingReference(BorrowingReference&& ref)
40
- : _value(ref._value), _isDeleted(ref._isDeleted), _strongRefCount(ref._strongRefCount), _weakRefCount(ref._weakRefCount),
41
- _mutex(ref._mutex) {
49
+ BorrowingReference(BorrowingReference&& ref) : _value(ref._value), _state(ref._state) {
42
50
  ref._value = nullptr;
43
- ref._isDeleted = nullptr;
44
- ref._strongRefCount = nullptr;
45
- ref._weakRefCount = nullptr;
51
+ ref._state = nullptr;
46
52
  }
47
53
 
48
54
  BorrowingReference& operator=(const BorrowingReference& ref) {
49
55
  if (this == &ref)
50
56
  return *this;
51
57
 
52
- if (_weakRefCount != nullptr) {
58
+ if (_state != nullptr) {
53
59
  // destroy previous pointer
54
- (*_weakRefCount)--;
55
- maybeDestroy();
60
+ bool shouldDestroy = _state->decrementStrongRefCount();
61
+ if (shouldDestroy) {
62
+ forceDestroyValue();
63
+ }
64
+ maybeDestroyState();
56
65
  }
57
66
 
58
67
  _value = ref._value;
59
- _isDeleted = ref._isDeleted;
60
- _strongRefCount = ref._strongRefCount;
61
- _weakRefCount = ref._weakRefCount;
62
- _mutex = ref._mutex;
63
- if (_weakRefCount != nullptr) {
68
+ _state = ref._state;
69
+ if (_state != nullptr) {
64
70
  // increment new pointer
65
- (*_weakRefCount)++;
71
+ _state->strongRefCount++;
66
72
  }
67
73
 
68
74
  return *this;
69
75
  }
70
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:
71
94
  ~BorrowingReference() {
72
- if (_weakRefCount == nullptr) {
95
+ if (_state == nullptr) {
73
96
  // we are just a dangling nullptr.
74
97
  return;
75
98
  }
76
99
 
77
- (*_weakRefCount)--;
78
- maybeDestroy();
100
+ // decrement strong ref count on destroy
101
+ bool shouldDestroy = _state->decrementStrongRefCount();
102
+ if (shouldDestroy) {
103
+ forceDestroyValue();
104
+ }
105
+ maybeDestroyState();
79
106
  }
80
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:
81
118
  /**
82
- Try to lock the borrowing reference to an owning reference, or `nullptr` if it has already been deleted.
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.
83
123
  */
84
124
  [[nodiscard]]
85
- OwningReference<T> lock() const;
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;
134
+ }
135
+
136
+ /**
137
+ Get a borrowing (or "weak") reference to this owning reference
138
+ */
139
+ [[nodiscard]]
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
+ }
86
155
 
87
156
  public:
88
- friend class OwningReference<T>;
157
+ explicit inline operator bool() const {
158
+ return hasValue();
159
+ }
89
160
 
90
- private:
91
- void maybeDestroy() {
92
- _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
+ }
93
194
 
94
- if (*_strongRefCount == 0 && *_weakRefCount == 0) {
195
+ private:
196
+ void maybeDestroyState() {
197
+ if (_state->strongRefCount == 0 && _state->weakRefCount == 0) {
95
198
  // free the full memory if there are no more references at all
96
- if (!(*_isDeleted)) {
97
- delete _value;
98
- }
99
- delete _isDeleted;
100
- delete _strongRefCount;
101
- delete _weakRefCount;
102
- _mutex->unlock();
199
+ delete _state;
200
+ _state = nullptr;
103
201
  return;
104
202
  }
203
+ }
105
204
 
106
- _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;
107
213
  }
108
214
 
215
+ public:
216
+ friend class WeakReference<T>;
217
+ friend class OwningLock<T>;
218
+
109
219
  private:
110
220
  T* _value;
111
- bool* _isDeleted;
112
- std::atomic_size_t* _strongRefCount;
113
- std::atomic_size_t* _weakRefCount;
114
- std::recursive_mutex* _mutex;
221
+ ReferenceState* _state;
115
222
  };
116
223
 
117
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.21.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
@@ -31,6 +31,7 @@
31
31
  #define _CXX_INTEROP_HAS_ATTRIBUTE(x) 0
32
32
  #endif
33
33
 
34
+ // Closed/Final Enums
34
35
  #if _CXX_INTEROP_HAS_ATTRIBUTE(enum_extensibility)
35
36
  // Enum is marked as closed/not extensible
36
37
  #define CLOSED_ENUM __attribute__((enum_extensibility(closed)))
@@ -38,6 +39,7 @@
38
39
  #define CLOSED_ENUM
39
40
  #endif
40
41
 
42
+ // Swift Support
41
43
  #if __has_include(<swift/bridging>)
42
44
  // Swift's bridging header defines those things
43
45
  #include <swift/bridging>
@@ -50,4 +52,11 @@
50
52
  #define SWIFT_NONCOPYABLE
51
53
  #endif
52
54
 
55
+ // React Native Support (right now it's only stubbed out)
56
+ #ifndef REACT_NATIVE_VERSION_MINOR
57
+ #define REACT_NATIVE_VERSION_MAJOR 0
58
+ #define REACT_NATIVE_VERSION_MINOR 0
59
+ #define REACT_NATIVE_VERSION_PATCH 0
60
+ #endif
61
+
53
62
  #endif /* NitroDefines_h */
@@ -9,6 +9,7 @@
9
9
 
10
10
  #include <cstddef>
11
11
  #include <cstdint>
12
+ #include <string>
12
13
 
13
14
  namespace margelo::nitro {
14
15
 
@@ -40,4 +41,20 @@ constexpr uint64_t hashString(const char (&str)[N]) {
40
41
  return hashString(str, N - 1); // N includes the null terminator, so subtract 1
41
42
  }
42
43
 
44
+ /**
45
+ * Hashes the given `string_view` using the FNV-1a hashing algorithm.
46
+ * This can be constexpr.
47
+ */
48
+ constexpr uint64_t hashString(const std::string_view& string) {
49
+ return hashString(string.data(), string.length());
50
+ }
51
+
52
+ /**
53
+ * Hashes the given `string` using the FNV-1a hashing algorithm.
54
+ * This happens at runtime.
55
+ */
56
+ inline uint64_t hashString(const std::string& string) {
57
+ return hashString(string.c_str(), string.length());
58
+ }
59
+
43
60
  } // namespace margelo::nitro
@@ -1,5 +1,5 @@
1
1
  //
2
- // TypeInfo.hpp
2
+ // NitroTypeInfo.hpp
3
3
  // Nitro
4
4
  //
5
5
  // Created by Marc Rousavy on 17.07.24.
@@ -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