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.
@@ -11,38 +11,58 @@
11
11
 
12
12
  namespace margelo::nitro {
13
13
 
14
- ThreadPool::ThreadPool(const char* name, size_t numThreads) : _isAlive(true), _name(name) {
15
- Logger::log(LogLevel::Info, TAG, "Creating ThreadPool \"%s\" with %i threads...", name, numThreads);
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 < numThreads; ++i) {
18
- std::string threadName = std::string(name) + "-" + std::to_string(i + 1);
19
- _workers.emplace_back([this, threadName] {
20
- // Set the Thread's name
21
- ThreadUtils::setThreadName(threadName);
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
- // Start the run-loop
24
- while (true) {
25
- std::function<void()> task;
26
- {
27
- // Lock on the mutex so only one Worker receives the condition signal at a time
28
- std::unique_lock<std::mutex> lock(_queueMutex);
29
- this->_condition.wait(lock, [this] { return !_isAlive || !_tasks.empty(); });
30
- if (!_isAlive && _tasks.empty()) {
31
- // ThreadPool is dead - stop run-loop.
32
- return;
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
- // Run it (outside of the mutex so others can run in parallel)
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
- std::shared_ptr<ThreadPool> ThreadPool::getSharedPool() {
75
- static std::shared_ptr<ThreadPool> shared;
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 fixed workers/threads.
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 numThreads);
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 std::shared_ptr<ThreadPool> getSharedPool();
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.16.2",
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.31.0"
73
+ "react-native-builder-bob": "^0.32.0"
74
74
  },
75
75
  "peerDependencies": {
76
76
  "react": "*",
@@ -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
@@ -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