react-native-nitro-modules 0.16.2 → 0.17.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.
- package/NitroModules.podspec +2 -1
- package/README.md +6 -6
- package/android/build.gradle +10 -1
- package/android/src/main/cpp/core/JPromise.hpp +62 -5
- package/android/src/main/cpp/utils/JUnit.hpp +38 -0
- package/android/src/main/java/com/margelo/nitro/core/Promise.kt +76 -10
- package/cpp/core/HybridFunction.hpp +1 -0
- package/cpp/core/Promise.cpp +10 -0
- package/cpp/core/Promise.hpp +388 -0
- package/cpp/jsi/JSIConverter+Exception.hpp +44 -0
- package/cpp/jsi/JSIConverter+Future.hpp +45 -0
- package/cpp/jsi/JSIConverter+Promise.hpp +76 -62
- package/cpp/jsi/JSIConverter.hpp +2 -0
- package/cpp/prototype/HybridObjectPrototype.cpp +1 -0
- package/cpp/threading/ThreadPool.cpp +47 -32
- package/cpp/threading/ThreadPool.hpp +15 -3
- package/ios/core/RuntimeError.swift +18 -0
- package/ios/utils/RuntimeError.hpp +23 -0
- package/ios/utils/SwiftClosure.hpp +5 -1
- package/package.json +2 -2
- package/cpp/jsi/JSPromise.cpp +0 -58
- package/cpp/jsi/JSPromise.hpp +0 -54
- package/ios/core/PromiseHolder.hpp +0 -86
|
@@ -11,38 +11,58 @@
|
|
|
11
11
|
|
|
12
12
|
namespace margelo::nitro {
|
|
13
13
|
|
|
14
|
-
ThreadPool::ThreadPool(const char* name, size_t
|
|
15
|
-
|
|
14
|
+
ThreadPool::ThreadPool(const char* name, size_t initialThreadsCount, size_t maxThreadsCount)
|
|
15
|
+
: _isAlive(true), _threadCountLimit(maxThreadsCount), _name(name) {
|
|
16
|
+
Logger::log(LogLevel::Info, TAG, "Creating ThreadPool \"%s\" with %i initial threads (max: %i)...", name, initialThreadsCount,
|
|
17
|
+
maxThreadsCount);
|
|
16
18
|
|
|
17
|
-
for (size_t i = 0; i <
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
for (size_t i = 0; i < initialThreadsCount; i++) {
|
|
20
|
+
addThread();
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
void ThreadPool::addThread() {
|
|
25
|
+
std::unique_lock<std::mutex> lock(_queueMutex);
|
|
26
|
+
if (!_isAlive) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
size_t i = ++_threadCount;
|
|
31
|
+
Logger::log(LogLevel::Info, TAG, "Adding Thread %i to ThreadPool \"%s\"...", i, _name);
|
|
32
|
+
|
|
33
|
+
std::string threadName = std::string(_name) + "-" + std::to_string(i);
|
|
34
|
+
_workers.emplace_back([this, threadName] {
|
|
35
|
+
// Set the Thread's name
|
|
36
|
+
ThreadUtils::setThreadName(threadName);
|
|
22
37
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
// Schedule the oldest task
|
|
35
|
-
task = std::move(_tasks.front());
|
|
36
|
-
_tasks.pop();
|
|
38
|
+
// Start the run-loop
|
|
39
|
+
while (true) {
|
|
40
|
+
std::function<void()> task;
|
|
41
|
+
{
|
|
42
|
+
// Lock on the mutex so only one Worker receives the condition signal at a time
|
|
43
|
+
std::unique_lock<std::mutex> lock(_queueMutex);
|
|
44
|
+
this->_condition.wait(lock, [this] { return !_isAlive || !_tasks.empty(); });
|
|
45
|
+
if (!_isAlive && _tasks.empty()) {
|
|
46
|
+
// ThreadPool is dead - stop run-loop.
|
|
47
|
+
return;
|
|
37
48
|
}
|
|
38
|
-
//
|
|
39
|
-
task();
|
|
49
|
+
// Schedule the oldest task
|
|
50
|
+
task = std::move(_tasks.front());
|
|
51
|
+
_tasks.pop();
|
|
40
52
|
}
|
|
41
|
-
|
|
42
|
-
|
|
53
|
+
// Run it (outside of the mutex so others can run in parallel)
|
|
54
|
+
task();
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
_condition.notify_all();
|
|
43
58
|
}
|
|
44
59
|
|
|
45
60
|
void ThreadPool::run(std::function<void()>&& task) {
|
|
61
|
+
// If there are tasks still waiting to be finished, just start a new Thread.
|
|
62
|
+
if (!_tasks.empty() && _threadCount < _threadCountLimit) {
|
|
63
|
+
addThread();
|
|
64
|
+
}
|
|
65
|
+
// New scope because of RAII lock
|
|
46
66
|
{
|
|
47
67
|
// lock on the mutex - we want to emplace the task back in the queue
|
|
48
68
|
std::unique_lock<std::mutex> lock(_queueMutex);
|
|
@@ -71,13 +91,8 @@ ThreadPool::~ThreadPool() {
|
|
|
71
91
|
}
|
|
72
92
|
}
|
|
73
93
|
|
|
74
|
-
|
|
75
|
-
static
|
|
76
|
-
if (shared == nullptr) {
|
|
77
|
-
int availableThreads = std::thread::hardware_concurrency();
|
|
78
|
-
auto numThreads = std::min(availableThreads, 3);
|
|
79
|
-
shared = std::make_shared<ThreadPool>("nitro-thread", numThreads);
|
|
80
|
-
}
|
|
94
|
+
ThreadPool& ThreadPool::shared() {
|
|
95
|
+
static ThreadPool shared("nitro-thread", 3, 10);
|
|
81
96
|
return shared;
|
|
82
97
|
}
|
|
83
98
|
|
|
@@ -22,10 +22,13 @@ namespace margelo::nitro {
|
|
|
22
22
|
class ThreadPool final {
|
|
23
23
|
public:
|
|
24
24
|
/**
|
|
25
|
-
* Create a new ThreadPool with the given number of
|
|
25
|
+
* Create a new ThreadPool with the given number of minimum workers/threads.
|
|
26
|
+
* The Thread Pool can expand on the fly if it is busy.
|
|
26
27
|
*/
|
|
27
|
-
explicit ThreadPool(const char* const name, size_t
|
|
28
|
+
explicit ThreadPool(const char* const name, size_t initialThreadsCount, size_t maxThreadsCount);
|
|
28
29
|
~ThreadPool();
|
|
30
|
+
ThreadPool(const ThreadPool&) = delete;
|
|
31
|
+
ThreadPool(ThreadPool&&) = delete;
|
|
29
32
|
|
|
30
33
|
/**
|
|
31
34
|
* Schedules the given task asynchronously on the ThreadPool.
|
|
@@ -33,12 +36,19 @@ public:
|
|
|
33
36
|
*/
|
|
34
37
|
void run(std::function<void()>&& task);
|
|
35
38
|
|
|
39
|
+
private:
|
|
40
|
+
/**
|
|
41
|
+
* Adds a new Thread to the current Thread Pool.
|
|
42
|
+
* This grows the size by one, and potentially starts work sooner if other Threads are busy.
|
|
43
|
+
*/
|
|
44
|
+
void addThread();
|
|
45
|
+
|
|
36
46
|
public:
|
|
37
47
|
/**
|
|
38
48
|
* Get a static singleton instance - a shared ThreadPool.
|
|
39
49
|
* The shared ThreadPool has 3 threads.
|
|
40
50
|
*/
|
|
41
|
-
static
|
|
51
|
+
static ThreadPool& shared();
|
|
42
52
|
|
|
43
53
|
private:
|
|
44
54
|
std::vector<std::thread> _workers;
|
|
@@ -46,6 +56,8 @@ private:
|
|
|
46
56
|
std::mutex _queueMutex;
|
|
47
57
|
std::condition_variable _condition;
|
|
48
58
|
std::atomic<bool> _isAlive;
|
|
59
|
+
std::atomic<size_t> _threadCount;
|
|
60
|
+
size_t _threadCountLimit;
|
|
49
61
|
const char* _name;
|
|
50
62
|
static constexpr auto TAG = "ThreadPool";
|
|
51
63
|
};
|
|
@@ -14,4 +14,22 @@ import Foundation
|
|
|
14
14
|
*/
|
|
15
15
|
public enum RuntimeError: Error {
|
|
16
16
|
case error(withMessage: String)
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Creates a new `RuntimeError` from the given C++ `std::exception`.
|
|
20
|
+
*/
|
|
21
|
+
public static func from(cppError: std.exception) -> RuntimeError {
|
|
22
|
+
let message = margelo.nitro.get_exception_message(cppError)
|
|
23
|
+
return .error(withMessage: String(message))
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public extension Error {
|
|
28
|
+
/**
|
|
29
|
+
* Converts this `Error` to a C++ `std::exception`.
|
|
30
|
+
*/
|
|
31
|
+
func toCpp() -> std.exception {
|
|
32
|
+
let message = String(describing: self)
|
|
33
|
+
return margelo.nitro.make_exception(std.string(message))
|
|
34
|
+
}
|
|
17
35
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
//
|
|
2
|
+
// RuntimeError.hpp
|
|
3
|
+
// NitroModules
|
|
4
|
+
//
|
|
5
|
+
// Created by Marc Rousavy on 19.11.24.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
#pragma once
|
|
9
|
+
|
|
10
|
+
#include <exception>
|
|
11
|
+
#include <string>
|
|
12
|
+
|
|
13
|
+
namespace margelo::nitro {
|
|
14
|
+
|
|
15
|
+
static inline std::exception make_exception(const std::string& message) {
|
|
16
|
+
return std::runtime_error(message);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
static inline std::string get_exception_message(const std::exception& exception) {
|
|
20
|
+
return exception.what();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
} // namespace margelo::nitro
|
|
@@ -55,7 +55,11 @@ public:
|
|
|
55
55
|
/**
|
|
56
56
|
* Gets the underlying `std::function`.
|
|
57
57
|
*/
|
|
58
|
-
const std::function<void()>& getFunction() {
|
|
58
|
+
const std::function<void()>& getFunction() const {
|
|
59
|
+
return _function;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
std::function<void()> getFunctionCopy() const {
|
|
59
63
|
return _function;
|
|
60
64
|
}
|
|
61
65
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-nitro-modules",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
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",
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"jest": "*",
|
|
71
71
|
"react": "*",
|
|
72
72
|
"react-native": "*",
|
|
73
|
-
"react-native-builder-bob": "^0.
|
|
73
|
+
"react-native-builder-bob": "^0.32.0"
|
|
74
74
|
},
|
|
75
75
|
"peerDependencies": {
|
|
76
76
|
"react": "*",
|
package/cpp/jsi/JSPromise.cpp
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
#include "JSPromise.hpp"
|
|
2
|
-
#include "JSICache.hpp"
|
|
3
|
-
#include "NitroLogger.hpp"
|
|
4
|
-
#include <jsi/jsi.h>
|
|
5
|
-
|
|
6
|
-
namespace margelo::nitro {
|
|
7
|
-
|
|
8
|
-
using namespace facebook;
|
|
9
|
-
|
|
10
|
-
JSPromise::JSPromise(jsi::Runtime& runtime, jsi::Function&& resolver, jsi::Function&& rejecter) {
|
|
11
|
-
auto functionCache = JSICache::getOrCreateCache(runtime);
|
|
12
|
-
_resolver = functionCache.makeShared(std::move(resolver));
|
|
13
|
-
_rejecter = functionCache.makeShared(std::move(rejecter));
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
jsi::Value JSPromise::createPromise(jsi::Runtime& runtime, RunPromise&& run) {
|
|
17
|
-
// Get Promise ctor from global
|
|
18
|
-
auto promiseCtor = runtime.global().getPropertyAsFunction(runtime, "Promise");
|
|
19
|
-
|
|
20
|
-
auto promiseCallback = jsi::Function::createFromHostFunction(
|
|
21
|
-
runtime, jsi::PropNameID::forUtf8(runtime, "PromiseCallback"), 2,
|
|
22
|
-
[run = std::move(run)](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, size_t count) -> jsi::Value {
|
|
23
|
-
// Get resolver and rejecter
|
|
24
|
-
auto resolver = arguments[0].getObject(runtime).getFunction(runtime);
|
|
25
|
-
auto rejecter = arguments[1].getObject(runtime).getFunction(runtime);
|
|
26
|
-
// Create `Promise` type that wraps the JSI callbacks
|
|
27
|
-
auto promise = std::make_shared<JSPromise>(runtime, std::move(resolver), std::move(rejecter));
|
|
28
|
-
// Call `run` callback
|
|
29
|
-
run(runtime, promise);
|
|
30
|
-
|
|
31
|
-
return jsi::Value::undefined();
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
return promiseCtor.callAsConstructor(runtime, promiseCallback);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
void JSPromise::resolve(jsi::Runtime& runtime, jsi::Value&& result) {
|
|
38
|
-
OwningLock<jsi::Function> lock = _resolver.lock();
|
|
39
|
-
|
|
40
|
-
if (!_resolver) {
|
|
41
|
-
Logger::log(LogLevel::Error, TAG, "Promise resolver function has already been deleted! Ignoring call..");
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
_resolver->call(runtime, std::move(result));
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
void JSPromise::reject(jsi::Runtime& runtime, std::string message) {
|
|
48
|
-
OwningLock<jsi::Function> lock = _rejecter.lock();
|
|
49
|
-
|
|
50
|
-
if (!_rejecter) {
|
|
51
|
-
Logger::log(LogLevel::Error, TAG, "Promise rejecter function has already been deleted! Ignoring call..");
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
jsi::JSError error(runtime, message);
|
|
55
|
-
_rejecter->call(runtime, error.value());
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
} // namespace margelo::nitro
|
package/cpp/jsi/JSPromise.hpp
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// JSPromise.hpp
|
|
3
|
-
// react-native-filament
|
|
4
|
-
//
|
|
5
|
-
// Created by Marc Rousavy on 11.03.24.
|
|
6
|
-
//
|
|
7
|
-
|
|
8
|
-
#pragma once
|
|
9
|
-
|
|
10
|
-
#include "OwningReference.hpp"
|
|
11
|
-
#include <functional>
|
|
12
|
-
#include <jsi/jsi.h>
|
|
13
|
-
#include <memory>
|
|
14
|
-
|
|
15
|
-
namespace margelo::nitro {
|
|
16
|
-
|
|
17
|
-
using namespace facebook;
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Represents a JS Promise.
|
|
21
|
-
*
|
|
22
|
-
* `JSPromise` is not thread-safe: It has to be resolved/rejected
|
|
23
|
-
* on the same thread and Runtime as it was created on.
|
|
24
|
-
*/
|
|
25
|
-
class JSPromise final {
|
|
26
|
-
public:
|
|
27
|
-
JSPromise(jsi::Runtime& runtime, jsi::Function&& resolver, jsi::Function&& rejecter);
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
Resolve the Promise with the given `jsi::Value`.
|
|
31
|
-
If the `jsi::Runtime` has already been deleted, this will do nothing.
|
|
32
|
-
*/
|
|
33
|
-
void resolve(jsi::Runtime& runtime, jsi::Value&& result);
|
|
34
|
-
/**
|
|
35
|
-
Resolve the Promise with the given error message.
|
|
36
|
-
If the `jsi::Runtime` has already been deleted, this will do nothing.
|
|
37
|
-
*/
|
|
38
|
-
void reject(jsi::Runtime& runtime, std::string error);
|
|
39
|
-
|
|
40
|
-
private:
|
|
41
|
-
OwningReference<jsi::Function> _resolver;
|
|
42
|
-
OwningReference<jsi::Function> _rejecter;
|
|
43
|
-
static constexpr auto TAG = "Promise";
|
|
44
|
-
|
|
45
|
-
public:
|
|
46
|
-
using RunPromise = std::function<void(jsi::Runtime& runtime, std::shared_ptr<JSPromise> promise)>;
|
|
47
|
-
/**
|
|
48
|
-
* Create a new Promise using the JS `Promise` constructor and runs the given `run` function.
|
|
49
|
-
* The resulting Promise should be returned to JS so it can be awaited.
|
|
50
|
-
*/
|
|
51
|
-
static jsi::Value createPromise(jsi::Runtime& runtime, RunPromise&& run);
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
} // namespace margelo::nitro
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// PromiseHolder.hpp
|
|
3
|
-
// Nitro
|
|
4
|
-
//
|
|
5
|
-
// Created by Marc Rousavy on 15.08.24.
|
|
6
|
-
//
|
|
7
|
-
|
|
8
|
-
#pragma once
|
|
9
|
-
|
|
10
|
-
#include <future>
|
|
11
|
-
#include <string>
|
|
12
|
-
|
|
13
|
-
namespace margelo::nitro {
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Holds a `std::promise` that can be accessed from Swift using proper ref management.
|
|
17
|
-
*/
|
|
18
|
-
template <typename T>
|
|
19
|
-
class PromiseHolder {
|
|
20
|
-
public:
|
|
21
|
-
/**
|
|
22
|
-
* Create a new PromiseHolder (and a new `std::promise<T>`).
|
|
23
|
-
*/
|
|
24
|
-
explicit PromiseHolder() {
|
|
25
|
-
_promise = std::make_shared<std::promise<T>>();
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Resolve the underlying `std::promise<T>` with `T`.
|
|
30
|
-
*/
|
|
31
|
-
void resolve(const T& result) const {
|
|
32
|
-
_promise->set_value(result);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Reject the underlying `std::promise<T>` with the given message.
|
|
37
|
-
*/
|
|
38
|
-
void reject(const std::string& message) const {
|
|
39
|
-
try {
|
|
40
|
-
throw std::runtime_error(message);
|
|
41
|
-
} catch (...) {
|
|
42
|
-
_promise->set_exception(std::current_exception());
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Get the `std::future<T>` of the underlying `std::promise<T>`.
|
|
48
|
-
* This can only be called once.
|
|
49
|
-
*/
|
|
50
|
-
std::future<T> getFuture() {
|
|
51
|
-
return _promise->get_future();
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
private:
|
|
55
|
-
std::shared_ptr<std::promise<T>> _promise;
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
// Specialization for `void` (no args to `resolve()`)
|
|
59
|
-
template <>
|
|
60
|
-
class PromiseHolder<void> {
|
|
61
|
-
public:
|
|
62
|
-
explicit PromiseHolder() {
|
|
63
|
-
_promise = std::make_shared<std::promise<void>>();
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
void resolve() const {
|
|
67
|
-
_promise->set_value();
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
void reject(const std::string& message) const {
|
|
71
|
-
try {
|
|
72
|
-
throw std::runtime_error(message);
|
|
73
|
-
} catch (...) {
|
|
74
|
-
_promise->set_exception(std::current_exception());
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
std::future<void> getFuture() {
|
|
79
|
-
return _promise->get_future();
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
private:
|
|
83
|
-
std::shared_ptr<std::promise<void>> _promise;
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
} // namespace margelo::nitro
|