diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/AnimatedSensor/AnimatedSensorModule.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/AnimatedSensor/AnimatedSensorModule.cpp index 456cdc27d9b..f7d75985fee 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/AnimatedSensor/AnimatedSensorModule.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/AnimatedSensor/AnimatedSensorModule.cpp @@ -57,7 +57,7 @@ jsi::Value AnimatedSensorModule::registerSensor( } value.setProperty(uiRuntime, "interfaceOrientation", orientationDegrees); - uiWorkletRuntime->runGuarded(serializableHandler, value); + uiWorkletRuntime->runSync(serializableHandler, value); }); if (sensorId != -1) { sensorsIds_.insert(sensorId); diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp index f12ff6445dd..1d173a361ff 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp @@ -1273,7 +1273,7 @@ jsi::Value ReanimatedModuleProxy::subscribeForKeyboardEvents( if (!strongThis) { return; } - strongThis->workletsModuleProxy_->getUIWorkletRuntime()->runGuarded( + strongThis->workletsModuleProxy_->getUIWorkletRuntime()->runSync( serializableHandler, jsi::Value(keyboardState), jsi::Value(height)); }, isStatusBarTranslucent.getBool(), diff --git a/packages/react-native-worklets/Common/cpp/worklets/NativeModules/JSIWorkletsModuleProxy.cpp b/packages/react-native-worklets/Common/cpp/worklets/NativeModules/JSIWorkletsModuleProxy.cpp index 77458b193f4..a670092b652 100644 --- a/packages/react-native-worklets/Common/cpp/worklets/NativeModules/JSIWorkletsModuleProxy.cpp +++ b/packages/react-native-worklets/Common/cpp/worklets/NativeModules/JSIWorkletsModuleProxy.cpp @@ -50,7 +50,7 @@ inline void scheduleOnUI( const auto scope = jsi::Scope(uiWorkletRuntime->getJSIRuntime()); #endif // JS_RUNTIME_HERMES - uiWorkletRuntime->runGuarded(serializableWorklet); + uiWorkletRuntime->runSync(serializableWorklet); }); } @@ -59,7 +59,10 @@ inline jsi::Value executeOnUIRuntimeSync( jsi::Runtime &rt, const jsi::Value &worklet) { if (auto uiWorkletRuntime = weakUIWorkletRuntime.lock()) { - return uiWorkletRuntime->executeSync(rt, worklet); + auto serializableWorklet = extractSerializableOrThrow( + rt, worklet, "[Worklets] Only worklets can be executed on UI runtime."); + auto serializedResult = uiWorkletRuntime->runSyncSerialized(serializableWorklet); + return serializedResult->toJSValue(rt); } return jsi::Value::undefined(); } @@ -85,10 +88,9 @@ inline jsi::Value propagateModuleUpdate( const auto runtimes = runtimeManager->getAllRuntimes(); for (auto runtime : runtimes) { - runtime->executeSync([code, sourceUrl](jsi::Runtime &rt) { + runtime->runSync([code, sourceUrl](jsi::Runtime &rt) -> void { const auto buffer = std::make_shared(code); rt.evaluateJavaScript(buffer, sourceUrl); - return jsi::Value::undefined(); }); } return jsi::Value::undefined(); diff --git a/packages/react-native-worklets/Common/cpp/worklets/SharedItems/Serializable.h b/packages/react-native-worklets/Common/cpp/worklets/SharedItems/Serializable.h index 44d0ee3ea55..b6c5d976512 100644 --- a/packages/react-native-worklets/Common/cpp/worklets/SharedItems/Serializable.h +++ b/packages/react-native-worklets/Common/cpp/worklets/SharedItems/Serializable.h @@ -19,9 +19,9 @@ jsi::Function getValueUnpacker(jsi::Runtime &rt); jsi::Function getCallGuard(jsi::Runtime &rt); #endif // NDEBUG -// If possible, please use `WorkletRuntime::runGuarded` instead. +/** If possible, please use `WorkletRuntime::runSync` instead. */ template -inline jsi::Value runOnRuntimeGuarded(jsi::Runtime &rt, const jsi::Value &function, Args &&...args) { +inline jsi::Value runOnRuntimeGuarded(jsi::Runtime &rt, const jsi::Function &function, Args &&...args) { // We only use callGuard in debug mode, otherwise we call the provided // function directly. CallGuard provides a way of capturing exceptions in // JavaScript and propagating them to the main React Native thread such that @@ -29,10 +29,16 @@ inline jsi::Value runOnRuntimeGuarded(jsi::Runtime &rt, const jsi::Value &functi #ifndef NDEBUG return getCallGuard(rt).call(rt, function, args...); #else - return function.asObject(rt).asFunction(rt).call(rt, args...); + return function..call(rt, args...); #endif // NDEBUG } +/** If possible, please use `WorkletRuntime::runSync` instead. */ +template +inline jsi::Value runOnRuntimeGuarded(jsi::Runtime &rt, const jsi::Value &function, Args &&...args) { + return runOnRuntimeGuarded(rt, function.asObject(rt).asFunction(rt), std::forward(args)...); +} + inline void cleanupIfRuntimeExists(jsi::Runtime *rt, std::unique_ptr &value) { if (rt != nullptr && !WorkletRuntimeRegistry::isRuntimeAlive(rt)) { // The below use of unique_ptr.release prevents the smart pointer from diff --git a/packages/react-native-worklets/Common/cpp/worklets/Tools/WorkletEventHandler.cpp b/packages/react-native-worklets/Common/cpp/worklets/Tools/WorkletEventHandler.cpp index d69b0004f9f..7401977458b 100644 --- a/packages/react-native-worklets/Common/cpp/worklets/Tools/WorkletEventHandler.cpp +++ b/packages/react-native-worklets/Common/cpp/worklets/Tools/WorkletEventHandler.cpp @@ -9,7 +9,7 @@ void WorkletEventHandler::process( const std::shared_ptr &workletRuntime, const double eventTimestamp, const jsi::Value &eventValue) const { - workletRuntime->runGuarded(handlerFunction_, jsi::Value(eventTimestamp), eventValue); + workletRuntime->runSync(handlerFunction_, jsi::Value(eventTimestamp), eventValue); } uint64_t WorkletEventHandler::getHandlerId() const { diff --git a/packages/react-native-worklets/Common/cpp/worklets/WorkletRuntime/RuntimeManager.cpp b/packages/react-native-worklets/Common/cpp/worklets/WorkletRuntime/RuntimeManager.cpp index bfcfabe418f..13d651ac37e 100644 --- a/packages/react-native-worklets/Common/cpp/worklets/WorkletRuntime/RuntimeManager.cpp +++ b/packages/react-native-worklets/Common/cpp/worklets/WorkletRuntime/RuntimeManager.cpp @@ -49,7 +49,7 @@ std::shared_ptr RuntimeManager::createWorkletRuntime( workletRuntime->init(std::move(jsiWorkletsModuleProxy)); if (initializer) { - workletRuntime->runGuarded(initializer); + workletRuntime->runSync(initializer); } registerRuntime(runtimeId, workletRuntime); diff --git a/packages/react-native-worklets/Common/cpp/worklets/WorkletRuntime/WorkletRuntime.cpp b/packages/react-native-worklets/Common/cpp/worklets/WorkletRuntime/WorkletRuntime.cpp index 2577a6ce939..c5a4dbdc57b 100644 --- a/packages/react-native-worklets/Common/cpp/worklets/WorkletRuntime/WorkletRuntime.cpp +++ b/packages/react-native-worklets/Common/cpp/worklets/WorkletRuntime/WorkletRuntime.cpp @@ -135,44 +135,68 @@ void WorkletRuntime::init(std::shared_ptr jsiWorkletsMod #endif // WORKLETS_BUNDLE_MODE } -void WorkletRuntime::runAsyncGuarded(const std::shared_ptr &worklet) { - react_native_assert( - "[Worklets] Tried to invoke `runAsyncGuarded` on a Worklet Runtime but " - "the async queue is not set. Recreate the runtime with a valid async queue."); +/* #region schedule */ - queue_->push([worklet, weakThis = weak_from_this()] { +void WorkletRuntime::schedule(jsi::Function &&function) const { + react_native_assert( + queue_ && + "[Worklets] Tried to invoke `schedule` on a Worklet Runtime but the " + "async queue is not set. Recreate the runtime with a valid async queue."); + queue_->push([function = std::make_shared(std::move(function)), weakThis = weak_from_this()]() { auto strongThis = weakThis.lock(); if (!strongThis) { return; } - strongThis->runGuarded(worklet); + strongThis->runSync(*function); }); } -jsi::Value WorkletRuntime::executeSync(jsi::Runtime &rt, const jsi::Value &worklet) const { - auto serializableWorklet = extractSerializableOrThrow( - rt, worklet, "[Worklets] Only worklets can be executed synchronously on UI runtime."); - auto lock = std::unique_lock(*runtimeMutex_); - jsi::Runtime &uiRuntime = getJSIRuntime(); - auto result = runGuarded(serializableWorklet); - auto serializableResult = extractSerializableOrThrow(uiRuntime, result); - lock.unlock(); - return serializableResult->toJSValue(rt); +void WorkletRuntime::schedule(std::shared_ptr worklet) const { + react_native_assert( + queue_ && + "[Worklets] Tried to invoke `schedule` on a Worklet Runtime but the " + "async queue is not set. Recreate the runtime with a valid async queue."); + + queue_->push([worklet = std::move(worklet), weakThis = weak_from_this()] { + auto strongThis = weakThis.lock(); + if (!strongThis) { + return; + } + + strongThis->runSync(worklet); + }); } -jsi::Value WorkletRuntime::executeSync(std::function &&job) const { - auto lock = std::unique_lock(*runtimeMutex_); - jsi::Runtime &uiRuntime = getJSIRuntime(); - return job(uiRuntime); +void WorkletRuntime::schedule(std::function job) const { + react_native_assert( + queue_ && + "[Worklets] Tried to invoke `schedule` on a Worklet Runtime but the " + "async queue is not set. Recreate the runtime with a valid async queue."); + + queue_->push(std::move(job)); } -jsi::Value WorkletRuntime::executeSync(const std::function &job) const { - auto lock = std::unique_lock(*runtimeMutex_); - jsi::Runtime &uiRuntime = getJSIRuntime(); - return job(uiRuntime); +void WorkletRuntime::schedule(std::function job) const { + react_native_assert( + queue_ && + "[Worklets] Tried to invoke `schedule` on a Worklet Runtime but the " + "async queue is not set. Recreate the runtime with a valid async queue."); + + queue_->push([job = std::move(job), weakThis = weak_from_this()]() { + auto strongThis = weakThis.lock(); + if (!strongThis) { + return; + } + + auto lock = std::unique_lock(*strongThis->runtimeMutex_); + jsi::Runtime &runtime = strongThis->getJSIRuntime(); + job(runtime); + }); } +/* #endregion */ + jsi::Value WorkletRuntime::get(jsi::Runtime &rt, const jsi::PropNameID &propName) { auto name = propName.utf8(rt); if (name == "toString") { @@ -215,7 +239,7 @@ void scheduleOnRuntime( rt, serializableWorkletValue, "[Worklets] Function passed to `_scheduleOnRuntime` is not a serializable worklet."); - workletRuntime->runAsyncGuarded(serializableWorklet); + workletRuntime->schedule(serializableWorklet); } #if REACT_NATIVE_MINOR_VERSION >= 81 @@ -231,4 +255,27 @@ std::weak_ptr WorkletRuntime::getWeakRuntimeFromJSIRuntime(jsi:: } #endif // REACT_NATIVE_MINOR_VERSION >= 81 +/* #region deprecated */ + +void WorkletRuntime::runAsyncGuarded(const std::shared_ptr &worklet) { + schedule(worklet); +} + +jsi::Value WorkletRuntime::executeSync(jsi::Runtime &caller, const jsi::Value &worklet) const { + auto serializableWorklet = extractSerializableOrThrow( + caller, worklet, "[Worklets] Only worklets can be executed synchronously on UI runtime."); + auto result = runSyncSerialized(serializableWorklet); + return result->toJSValue(caller); +} + +jsi::Value WorkletRuntime::executeSync(std::function &&job) const { + return runSync(job); +} + +jsi::Value WorkletRuntime::executeSync(const std::function &job) const { + return runSync(job); +} + +/* #endregion */ + } // namespace worklets diff --git a/packages/react-native-worklets/Common/cpp/worklets/WorkletRuntime/WorkletRuntime.h b/packages/react-native-worklets/Common/cpp/worklets/WorkletRuntime/WorkletRuntime.h index 7818727f66b..5eba3d2a437 100644 --- a/packages/react-native-worklets/Common/cpp/worklets/WorkletRuntime/WorkletRuntime.h +++ b/packages/react-native-worklets/Common/cpp/worklets/WorkletRuntime/WorkletRuntime.h @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -22,6 +23,16 @@ using namespace react; namespace worklets { +template +concept ImplicitlySerializableCallable = std::is_assignable_v || + std::is_assignable_v &, TCallable>; + +template +concept RuntimeCallable = requires(TCallable &&callable, jsi::Runtime &rt) { + // NOLINTNEXTLINE(readability/braces) cpplint doesn't understand concepts + { callable(rt) }; +} || ImplicitlySerializableCallable; + /** * Forward declaration to avoid circular dependencies. */ @@ -29,49 +40,117 @@ class JSIWorkletsModuleProxy; class WorkletRuntime : public jsi::HostObject, public std::enable_shared_from_this { public: - explicit WorkletRuntime( - uint64_t runtimeId, - const std::shared_ptr &jsQueue, - const std::string &name, - const std::shared_ptr &queue = nullptr, - bool enableEventLoop = true); + void schedule(jsi::Function &&function) const; + void schedule(std::shared_ptr worklet) const; + void schedule(std::function job) const; + void schedule(std::function job) const; - void init(std::shared_ptr jsiWorkletsModuleProxy); + /* #region runSync */ - jsi::Runtime &getJSIRuntime() const { - return *runtime_; + template + std::invoke_result_t inline runSync(TCallable &&callable, Args &&...args) const; + template + jsi::Value inline runSync(const jsi::Function &function, Args &&...args) const { + auto &rt = *runtime_; + return runOnRuntimeGuarded(rt, function, std::forward(args)...); } - template - inline jsi::Value runGuarded(const std::shared_ptr &serializableWorklet, Args &&...args) const { + jsi::Value inline runSync(const std::shared_ptr &worklet, Args &&...args) const { jsi::Runtime &rt = *runtime_; - return runOnRuntimeGuarded(rt, serializableWorklet->toJSValue(rt), std::forward(args)...); + return runSync(worklet->toJSValue(rt).asObject(rt).asFunction(rt), std::forward(args)...); + } + template + std::invoke_result_t inline runSync(TCallable &&job) const { + jsi::Runtime &rt = getJSIRuntime(); + auto lock = std::unique_lock(*runtimeMutex_); + return job(rt); } - void runAsyncGuarded(const std::shared_ptr &worklet); - - jsi::Value executeSync(jsi::Runtime &rt, const jsi::Value &worklet) const; - - jsi::Value executeSync(std::function &&job) const; + /* #endregion */ - jsi::Value executeSync(const std::function &job) const; + /* #region runSyncSerialized */ - std::string toString() const { - return "[WorkletRuntime \"" + name_ + "\"]"; + template + std::shared_ptr inline runSyncSerialized(TCallable &&callable, Args &&...args) const; + template + std::shared_ptr inline runSyncSerialized(const jsi::Function &function, Args &&...args) const { + jsi::Runtime &rt = getJSIRuntime(); + auto lock = std::unique_lock(*runtimeMutex_); + auto result = runSync(function, std::forward(args)...); + auto serializableResult = extractSerializableOrThrow( + rt, + result, + "[Worklets] Function passed to `runSyncSerialized`" + "must return a value serialized with `createSerializable`."); + return serializableResult; + } + template + std::shared_ptr inline runSyncSerialized( + const std::shared_ptr &worklet, + Args &&...args) const { + jsi::Runtime &rt = getJSIRuntime(); + auto lock = std::unique_lock(*runtimeMutex_); + auto result = runSync(worklet, std::forward(args)...); + auto serializableResult = extractSerializableOrThrow( + rt, + result, + "[Worklets] Worklet passed to `runSyncSerialized`" + "must return a value serialized with `createSerializable`."); + return serializableResult; } + /* #endregion */ + jsi::Value get(jsi::Runtime &rt, const jsi::PropNameID &propName) override; std::vector getPropertyNames(jsi::Runtime &rt) override; - [[nodiscard]] auto getRuntimeId() const -> uint64_t { + [[nodiscard]] inline std::string toString() const noexcept { + return "[WorkletRuntime \"" + name_ + "\"]"; + } + + [[nodiscard]] inline jsi::Runtime &getJSIRuntime() const noexcept { + return *runtime_; + } + + [[nodiscard]] inline uint64_t getRuntimeId() const noexcept { return runtimeId_; } - [[nodiscard]] auto getRuntimeName() const -> std::string { + [[nodiscard]] inline std::string getRuntimeName() const noexcept { return name_; } + explicit WorkletRuntime( + uint64_t runtimeId, + const std::shared_ptr &jsQueue, + const std::string &name, + const std::shared_ptr &queue = nullptr, + bool enableEventLoop = true); + + void init(std::shared_ptr jsiWorkletsModuleProxy); + + /* #region deprecated */ + + /** @deprecated Use `runSync` instead. */ + template + inline jsi::Value runGuarded(TCallable &&callable, Args &&...args) const { + return runSync(std::forward(callable), std::forward(args)...); + } + + /** @deprecated Use `schedule` instead. */ + void runAsyncGuarded(const std::shared_ptr &worklet); + + /** @deprecated Use `runSyncSerialized` and extract to `jsi::Value` with + * `extractSerializableOrThrow` instead. */ + jsi::Value executeSync(jsi::Runtime &rt, const jsi::Value &worklet) const; + /** @deprecated Use `runSync` instead. */ + jsi::Value executeSync(std::function &&job) const; + /** @deprecated Use `runSync` instead. */ + jsi::Value executeSync(const std::function &job) const; + + /* #endregion */ + #if REACT_NATIVE_MINOR_VERSION >= 81 /** * Retrieves a weak reference to the WorkletRuntime associated with the @@ -99,5 +178,4 @@ void scheduleOnRuntime( jsi::Runtime &rt, const jsi::Value &workletRuntimeValue, const jsi::Value &serializableWorkletValue); - } // namespace worklets