react-native-nitro-modules 0.16.2 → 0.18.0

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.
@@ -0,0 +1,422 @@
1
+ //
2
+ // Created by Marc Rousavy on 18.11.24.
3
+ //
4
+
5
+ #pragma once
6
+
7
+ #include "AssertPromiseState.hpp"
8
+ #include "NitroDefines.hpp"
9
+ #include "ThreadPool.hpp"
10
+ #include "TypeInfo.hpp"
11
+ #include <exception>
12
+ #include <future>
13
+ #include <jsi/jsi.h>
14
+ #include <memory>
15
+ #include <mutex>
16
+ #include <variant>
17
+
18
+ namespace margelo::nitro {
19
+
20
+ using namespace facebook;
21
+
22
+ template <typename TResult>
23
+ class Promise final {
24
+ public:
25
+ using OnResolvedFunc = std::function<void(const TResult&)>;
26
+ using OnRejectedFunc = std::function<void(const std::exception_ptr&)>;
27
+
28
+ public:
29
+ // Promise cannot be copied.
30
+ Promise(const Promise&) = delete;
31
+ // Promise can be moved.
32
+ Promise(Promise&&) = default;
33
+
34
+ private:
35
+ Promise() {
36
+ _mutex = std::make_unique<std::mutex>();
37
+ }
38
+
39
+ public:
40
+ /**
41
+ * Creates a new pending Promise that has to be resolved
42
+ * or rejected with `resolve(..)` or `reject(..)`.
43
+ */
44
+ static std::shared_ptr<Promise> create() {
45
+ return std::shared_ptr<Promise>(new Promise());
46
+ }
47
+
48
+ /**
49
+ * Creates a Promise that runs the given function `run` on a separate Thread pool.
50
+ */
51
+ static std::shared_ptr<Promise> async(std::function<TResult()>&& run) {
52
+ auto promise = create();
53
+ ThreadPool::shared().run([run = std::move(run), promise]() {
54
+ try {
55
+ // Run the code, then resolve.
56
+ TResult result = run();
57
+ promise->resolve(std::move(result));
58
+ } catch (...) {
59
+ // It threw an error.
60
+ promise->reject(std::current_exception());
61
+ }
62
+ });
63
+ return promise;
64
+ }
65
+
66
+ /**
67
+ * Creates a Promise and awaits the given future on a background Thread.
68
+ * Once the future resolves or rejects, the Promise resolves or rejects.
69
+ */
70
+ static std::shared_ptr<Promise> awaitFuture(std::future<TResult>&& future) {
71
+ auto sharedFuture = std::make_shared<std::future<TResult>>(std::move(future));
72
+ return async([sharedFuture = std::move(sharedFuture)]() { return sharedFuture->get(); });
73
+ }
74
+
75
+ /**
76
+ * Creates an immediately resolved Promise.
77
+ */
78
+ static std::shared_ptr<Promise> resolved(TResult&& result) {
79
+ auto promise = create();
80
+ promise->resolve(std::move(result));
81
+ return promise;
82
+ }
83
+ /**
84
+ * Creates an immediately rejected Promise.
85
+ */
86
+ static std::shared_ptr<Promise> rejected(const std::exception_ptr& error) {
87
+ auto promise = create();
88
+ promise->reject(error);
89
+ return promise;
90
+ }
91
+
92
+ public:
93
+ /**
94
+ * Resolves this Promise with the given result, and calls any pending listeners.
95
+ */
96
+ void resolve(TResult&& result) {
97
+ std::unique_lock lock(*_mutex);
98
+ #ifdef NITRO_DEBUG
99
+ assertPromiseState(*this, PromiseTask::WANTS_TO_RESOLVE);
100
+ #endif
101
+ _state = std::move(result);
102
+ for (const auto& onResolved : _onResolvedListeners) {
103
+ onResolved(std::get<TResult>(_state));
104
+ }
105
+ }
106
+ void resolve(const TResult& result) {
107
+ std::unique_lock lock(*_mutex);
108
+ #ifdef NITRO_DEBUG
109
+ assertPromiseState(*this, PromiseTask::WANTS_TO_RESOLVE);
110
+ #endif
111
+ _state = result;
112
+ for (const auto& onResolved : _onResolvedListeners) {
113
+ onResolved(std::get<TResult>(_state));
114
+ }
115
+ }
116
+ /**
117
+ * Rejects this Promise with the given error, and calls any pending listeners.
118
+ */
119
+ void reject(const std::exception& exception) {
120
+ std::unique_lock lock(*_mutex);
121
+ #ifdef NITRO_DEBUG
122
+ assertPromiseState(*this, PromiseTask::WANTS_TO_REJECT);
123
+ #endif
124
+ _state = std::make_exception_ptr(exception);
125
+ for (const auto& onRejected : _onRejectedListeners) {
126
+ onRejected(std::get<std::exception_ptr>(_state));
127
+ }
128
+ }
129
+ void reject(const std::exception_ptr& exception) {
130
+ std::unique_lock lock(*_mutex);
131
+ #ifdef NITRO_DEBUG
132
+ assertPromiseState(*this, PromiseTask::WANTS_TO_REJECT);
133
+ #endif
134
+ _state = exception;
135
+ for (const auto& onRejected : _onRejectedListeners) {
136
+ onRejected(exception);
137
+ }
138
+ }
139
+
140
+ public:
141
+ /**
142
+ * Add a listener that will be called when the Promise gets resolved.
143
+ * If the Promise is already resolved, the listener will be immediately called.
144
+ */
145
+ void addOnResolvedListener(OnResolvedFunc&& onResolved) {
146
+ std::unique_lock lock(*_mutex);
147
+ if (std::holds_alternative<TResult>(_state)) {
148
+ // Promise is already resolved! Call the callback immediately
149
+ onResolved(std::get<TResult>(_state));
150
+ } else {
151
+ // Promise is not yet resolved, put the listener in our queue.
152
+ _onResolvedListeners.push_back(std::move(onResolved));
153
+ }
154
+ }
155
+ void addOnResolvedListener(const OnResolvedFunc& onResolved) {
156
+ std::unique_lock lock(*_mutex);
157
+ if (std::holds_alternative<TResult>(_state)) {
158
+ // Promise is already resolved! Call the callback immediately
159
+ onResolved(std::get<TResult>(_state));
160
+ } else {
161
+ // Promise is not yet resolved, put the listener in our queue.
162
+ _onResolvedListeners.push_back(onResolved);
163
+ }
164
+ }
165
+ void addOnResolvedListenerCopy(const std::function<void(TResult)>& onResolved) {
166
+ std::unique_lock lock(*_mutex);
167
+ if (std::holds_alternative<TResult>(_state)) {
168
+ // Promise is already resolved! Call the callback immediately
169
+ onResolved(std::get<TResult>(_state));
170
+ } else {
171
+ // Promise is not yet resolved, put the listener in our queue.
172
+ _onResolvedListeners.push_back(onResolved);
173
+ }
174
+ }
175
+
176
+ /**
177
+ * Add a listener that will be called when the Promise gets rejected.
178
+ * If the Promise is already rejected, the listener will be immediately called.
179
+ */
180
+ void addOnRejectedListener(OnRejectedFunc&& onRejected) {
181
+ std::unique_lock lock(*_mutex);
182
+ if (std::holds_alternative<std::exception_ptr>(_state)) {
183
+ // Promise is already rejected! Call the callback immediately
184
+ onRejected(std::get<std::exception_ptr>(_state));
185
+ } else {
186
+ // Promise is not yet rejected, put the listener in our queue.
187
+ _onRejectedListeners.push_back(std::move(onRejected));
188
+ }
189
+ }
190
+ void addOnRejectedListener(const OnRejectedFunc& onRejected) {
191
+ std::unique_lock lock(*_mutex);
192
+ if (std::holds_alternative<std::exception_ptr>(_state)) {
193
+ // Promise is already rejected! Call the callback immediately
194
+ onRejected(std::get<std::exception_ptr>(_state));
195
+ } else {
196
+ // Promise is not yet rejected, put the listener in our queue.
197
+ _onRejectedListeners.push_back(onRejected);
198
+ }
199
+ }
200
+
201
+ public:
202
+ /**
203
+ * Gets an awaitable `std::future<T>` for this `Promise<T>`.
204
+ */
205
+ std::future<TResult> await() {
206
+ auto promise = std::make_shared<std::promise<TResult>>();
207
+ addOnResolvedListener([promise](const TResult& result) { promise->set_value(result); });
208
+ addOnRejectedListener([promise](const std::exception_ptr& error) { promise->set_exception(error); });
209
+ return promise->get_future();
210
+ }
211
+
212
+ public:
213
+ /**
214
+ * Get the result of the Promise if it has been resolved.
215
+ * If the Promise is not resolved, this will throw.
216
+ */
217
+ inline const TResult& getResult() {
218
+ if (!isResolved()) {
219
+ throw std::runtime_error("Cannot get result when Promise is not yet resolved!");
220
+ }
221
+ return std::get<TResult>(_state);
222
+ }
223
+ /**
224
+ * Get the error of the Promise if it has been rejected.
225
+ * If the Promise is not rejected, this will throw.
226
+ */
227
+ inline const std::exception_ptr& getError() {
228
+ if (!isRejected()) {
229
+ throw std::runtime_error("Cannot get error when Promise is not yet rejected!");
230
+ }
231
+ return std::get<std::exception_ptr>(_state);
232
+ }
233
+
234
+ public:
235
+ /**
236
+ * Gets whether this Promise has been successfully resolved with a result, or not.
237
+ */
238
+ [[nodiscard]]
239
+ inline bool isResolved() const noexcept {
240
+ return std::holds_alternative<TResult>(_state);
241
+ }
242
+ /**
243
+ * Gets whether this Promise has been rejected with an error, or not.
244
+ */
245
+ [[nodiscard]]
246
+ inline bool isRejected() const noexcept {
247
+ return std::holds_alternative<std::exception_ptr>(_state);
248
+ }
249
+ /**
250
+ * Gets whether this Promise has not yet been resolved nor rejected.
251
+ */
252
+ [[nodiscard]]
253
+ inline bool isPending() const noexcept {
254
+ return std::holds_alternative<std::monostate>(_state);
255
+ }
256
+
257
+ private:
258
+ std::variant<std::monostate, TResult, std::exception_ptr> _state;
259
+ std::vector<OnResolvedFunc> _onResolvedListeners;
260
+ std::vector<OnRejectedFunc> _onRejectedListeners;
261
+ std::unique_ptr<std::mutex> _mutex;
262
+ };
263
+
264
+ // Specialization for void
265
+ template <>
266
+ class Promise<void> final {
267
+ public:
268
+ using OnResolvedFunc = std::function<void()>;
269
+ using OnRejectedFunc = std::function<void(const std::exception_ptr&)>;
270
+
271
+ public:
272
+ Promise(const Promise&) = delete;
273
+ Promise(Promise&&) = default;
274
+
275
+ private:
276
+ Promise() {
277
+ _mutex = std::make_unique<std::mutex>();
278
+ }
279
+
280
+ public:
281
+ static std::shared_ptr<Promise> create() {
282
+ return std::shared_ptr<Promise>(new Promise());
283
+ }
284
+
285
+ static std::shared_ptr<Promise> async(std::function<void()>&& run) {
286
+ auto promise = create();
287
+ ThreadPool::shared().run([run = std::move(run), promise]() {
288
+ try {
289
+ // Run the code, then resolve.
290
+ run();
291
+ promise->resolve();
292
+ } catch (...) {
293
+ // It threw an error.
294
+ promise->reject(std::current_exception());
295
+ }
296
+ });
297
+ return promise;
298
+ }
299
+
300
+ static std::shared_ptr<Promise> awaitFuture(std::future<void>&& future) {
301
+ auto sharedFuture = std::make_shared<std::future<void>>(std::move(future));
302
+ return async([sharedFuture = std::move(sharedFuture)]() { sharedFuture->get(); });
303
+ }
304
+
305
+ static std::shared_ptr<Promise> resolved() {
306
+ auto promise = create();
307
+ promise->resolve();
308
+ return promise;
309
+ }
310
+ static std::shared_ptr<Promise> rejected(const std::exception_ptr& error) {
311
+ auto promise = create();
312
+ promise->reject(error);
313
+ return promise;
314
+ }
315
+
316
+ public:
317
+ void resolve() {
318
+ std::unique_lock lock(*_mutex);
319
+ #ifdef NITRO_DEBUG
320
+ assertPromiseState(*this, PromiseTask::WANTS_TO_RESOLVE);
321
+ #endif
322
+ _isResolved = true;
323
+ for (const auto& onResolved : _onResolvedListeners) {
324
+ onResolved();
325
+ }
326
+ }
327
+ void reject(const std::exception& exception) {
328
+ std::unique_lock lock(*_mutex);
329
+ #ifdef NITRO_DEBUG
330
+ assertPromiseState(*this, PromiseTask::WANTS_TO_REJECT);
331
+ #endif
332
+ _error = std::make_exception_ptr(exception);
333
+ for (const auto& onRejected : _onRejectedListeners) {
334
+ onRejected(_error.value());
335
+ }
336
+ }
337
+ void reject(const std::exception_ptr& exception) {
338
+ std::unique_lock lock(*_mutex);
339
+ #ifdef NITRO_DEBUG
340
+ assertPromiseState(*this, PromiseTask::WANTS_TO_REJECT);
341
+ #endif
342
+ _error = exception;
343
+ for (const auto& onRejected : _onRejectedListeners) {
344
+ onRejected(exception);
345
+ }
346
+ }
347
+
348
+ public:
349
+ void addOnResolvedListener(OnResolvedFunc&& onResolved) {
350
+ std::unique_lock lock(*_mutex);
351
+ if (_isResolved) {
352
+ onResolved();
353
+ } else {
354
+ _onResolvedListeners.push_back(std::move(onResolved));
355
+ }
356
+ }
357
+ void addOnResolvedListener(const OnResolvedFunc& onResolved) {
358
+ std::unique_lock lock(*_mutex);
359
+ if (_isResolved) {
360
+ onResolved();
361
+ } else {
362
+ _onResolvedListeners.push_back(onResolved);
363
+ }
364
+ }
365
+ void addOnRejectedListener(OnRejectedFunc&& onRejected) {
366
+ std::unique_lock lock(*_mutex);
367
+ if (_error.has_value()) {
368
+ onRejected(_error.value());
369
+ } else {
370
+ // Promise is not yet rejected, put the listener in our queue.
371
+ _onRejectedListeners.push_back(std::move(onRejected));
372
+ }
373
+ }
374
+ void addOnRejectedListener(const OnRejectedFunc& onRejected) {
375
+ std::unique_lock lock(*_mutex);
376
+ if (_error.has_value()) {
377
+ onRejected(_error.value());
378
+ } else {
379
+ // Promise is not yet rejected, put the listener in our queue.
380
+ _onRejectedListeners.push_back(onRejected);
381
+ }
382
+ }
383
+
384
+ public:
385
+ std::future<void> await() {
386
+ auto promise = std::make_shared<std::promise<void>>();
387
+ addOnResolvedListener([promise]() { promise->set_value(); });
388
+ addOnRejectedListener([promise](const std::exception_ptr& error) { promise->set_exception(error); });
389
+ return promise->get_future();
390
+ }
391
+
392
+ public:
393
+ inline const std::exception_ptr& getError() {
394
+ if (!isRejected()) {
395
+ throw std::runtime_error("Cannot get error when Promise is not yet rejected!");
396
+ }
397
+ return _error.value();
398
+ }
399
+
400
+ public:
401
+ [[nodiscard]]
402
+ inline bool isResolved() const noexcept {
403
+ return _isResolved;
404
+ }
405
+ [[nodiscard]]
406
+ inline bool isRejected() const noexcept {
407
+ return _error.has_value();
408
+ }
409
+ [[nodiscard]]
410
+ inline bool isPending() const noexcept {
411
+ return !isResolved() && !isRejected();
412
+ }
413
+
414
+ private:
415
+ std::unique_ptr<std::mutex> _mutex;
416
+ bool _isResolved = false;
417
+ std::optional<std::exception_ptr> _error;
418
+ std::vector<OnResolvedFunc> _onResolvedListeners;
419
+ std::vector<OnRejectedFunc> _onRejectedListeners;
420
+ };
421
+
422
+ } // namespace margelo::nitro
@@ -0,0 +1,54 @@
1
+ //
2
+ // Created by Marc Rousavy on 21.02.24.
3
+ //
4
+
5
+ #pragma once
6
+
7
+ // Forward declare a few of the common types that might have cyclic includes.
8
+ namespace margelo::nitro {
9
+ template <typename T, typename Enable>
10
+ struct JSIConverter;
11
+ } // namespace margelo::nitro
12
+
13
+ #include "JSIConverter.hpp"
14
+
15
+ #include "TypeInfo.hpp"
16
+ #include <exception>
17
+ #include <jsi/jsi.h>
18
+
19
+ namespace margelo::nitro {
20
+
21
+ using namespace facebook;
22
+
23
+ // std::exception_ptr <> Error
24
+ template <>
25
+ struct JSIConverter<std::exception_ptr> final {
26
+ static inline std::exception_ptr fromJSI(jsi::Runtime& runtime, const jsi::Value& error) {
27
+ jsi::Object object = error.asObject(runtime);
28
+ std::string name = object.getProperty(runtime, "name").asString(runtime).utf8(runtime);
29
+ std::string message = object.getProperty(runtime, "message").asString(runtime).utf8(runtime);
30
+ return std::make_exception_ptr(std::runtime_error(name + ": " + message));
31
+ }
32
+ static inline jsi::Value toJSI(jsi::Runtime& runtime, const std::exception_ptr& exception) {
33
+ try {
34
+ std::rethrow_exception(exception);
35
+ } catch (const std::exception& e) {
36
+ jsi::JSError error(runtime, e.what());
37
+ return jsi::Value(runtime, error.value());
38
+ } catch (...) {
39
+ // Some unknown exception was thrown - maybe an Objective-C error?
40
+ std::string errorName = TypeInfo::getCurrentExceptionName();
41
+ jsi::JSError error(runtime, "Unknown " + errorName + " error.");
42
+ return jsi::Value(runtime, error.value());
43
+ }
44
+ }
45
+ static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
46
+ if (!value.isObject()) {
47
+ return false;
48
+ }
49
+ jsi::Object object = value.getObject(runtime);
50
+ return object.hasProperty(runtime, "name") && object.hasProperty(runtime, "message");
51
+ }
52
+ };
53
+
54
+ } // namespace margelo::nitro
@@ -56,14 +56,12 @@ struct JSIConverter<std::function<ReturnType(Args...)>> final {
56
56
  }
57
57
 
58
58
  if constexpr (std::is_void_v<ResultingType>) {
59
- dispatcher->runAsync([&runtime, sharedFunction = std::move(sharedFunction), ... args = std::move(args)]() {
60
- callJSFunction(runtime, sharedFunction, args...);
61
- });
59
+ dispatcher->runAsync(
60
+ [&runtime, sharedFunction, ... args = std::move(args)]() { callJSFunction(runtime, sharedFunction, args...); });
62
61
  } else {
63
- return dispatcher->runAsyncAwaitable<ResultingType>(
64
- [&runtime, sharedFunction = std::move(sharedFunction), ... args = std::move(args)]() -> ResultingType {
65
- return callJSFunction(runtime, sharedFunction, args...);
66
- });
62
+ return dispatcher->runAsyncAwaitable<ResultingType>([&runtime, sharedFunction, ... args = std::move(args)]() -> ResultingType {
63
+ return callJSFunction(runtime, sharedFunction, args...);
64
+ });
67
65
  }
68
66
  };
69
67
  }
@@ -0,0 +1,45 @@
1
+ //
2
+ // Created by Marc Rousavy on 21.02.24.
3
+ //
4
+
5
+ #pragma once
6
+
7
+ // Forward declare a few of the common types that might have cyclic includes.
8
+ namespace margelo::nitro {
9
+
10
+ template <typename T, typename Enable>
11
+ struct JSIConverter;
12
+ } // namespace margelo::nitro
13
+
14
+ #include "JSIConverter.hpp"
15
+
16
+ #include "Promise.hpp"
17
+ #include <future>
18
+ #include <jsi/jsi.h>
19
+ #include <memory>
20
+
21
+ namespace margelo::nitro {
22
+
23
+ using namespace facebook;
24
+
25
+ // std::future<T> <> Promise<T>
26
+ template <typename TResult>
27
+ struct JSIConverter<std::future<TResult>> final {
28
+ [[deprecated("Use JSIConverter<std::shared_ptr<Promise<T>>> instead.")]]
29
+ static inline std::future<TResult> fromJSI(jsi::Runtime&, const jsi::Value&) {
30
+ throw std::runtime_error("Promise cannot be converted to a native type - it needs to be awaited first!");
31
+ }
32
+
33
+ [[deprecated("Use JSIConverter<std::shared_ptr<Promise<T>>> instead.")]]
34
+ static inline jsi::Value toJSI(jsi::Runtime& runtime, std::future<TResult>&& arg) {
35
+ auto promise = Promise<TResult>::awaitFuture(std::move(arg));
36
+ return JSIConverter<std::shared_ptr<Promise<TResult>>>::toJSI(runtime, promise);
37
+ }
38
+
39
+ [[deprecated("Use JSIConverter<std::shared_ptr<Promise<T>>> instead.")]]
40
+ static inline bool canConvert(jsi::Runtime&, const jsi::Value&) {
41
+ throw std::runtime_error("jsi::Value of type Promise cannot be converted to std::future yet!");
42
+ }
43
+ };
44
+
45
+ } // namespace margelo::nitro