diff --git a/packages/react-native-worklets/Common/cpp/worklets/Compat/StableApi.cpp b/packages/react-native-worklets/Common/cpp/worklets/Compat/StableApi.cpp index 94cd59de0145..67f1840209f6 100644 --- a/packages/react-native-worklets/Common/cpp/worklets/Compat/StableApi.cpp +++ b/packages/react-native-worklets/Common/cpp/worklets/Compat/StableApi.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include 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 2f45bfa67ae7..4d001c8df8b0 100644 --- a/packages/react-native-worklets/Common/cpp/worklets/NativeModules/JSIWorkletsModuleProxy.cpp +++ b/packages/react-native-worklets/Common/cpp/worklets/NativeModules/JSIWorkletsModuleProxy.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -348,11 +349,11 @@ jsi::Object JSIWorkletsModuleProxy::toOptimizedObject(jsi::Runtime &rt) const { } if (hostRuntimeId == RuntimeData::rnRuntimeId) { const int remoteId = static_cast(at<1>(args).getNumber()); - auto ref = makeSerializableRemoteFunction(rt, name, remoteId, jsScheduler); + auto ref = makeRNOriginSerializableRemoteFunction(rt, name, remoteId, jsScheduler); ref.asObject(rt).setProperty(rt, "__keepAlive", true); return ref; } - return makeSerializableRemoteFunction(rt, name, std::move(fun), hostRuntimeId); + return makeWorkletOriginSerializableRemoteFunction(rt, name, std::move(fun), hostRuntimeId); }); jsi_utils::addMethod<2>( diff --git a/packages/react-native-worklets/Common/cpp/worklets/SharedItems/Serializable.cpp b/packages/react-native-worklets/Common/cpp/worklets/SharedItems/Serializable.cpp index 3114a09c60f2..4c854239335c 100644 --- a/packages/react-native-worklets/Common/cpp/worklets/SharedItems/Serializable.cpp +++ b/packages/react-native-worklets/Common/cpp/worklets/SharedItems/Serializable.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -22,18 +23,6 @@ jsi::Function getValueUnpacker(jsi::Runtime &rt) { return valueUnpacker.asObject(rt).asFunction(rt); } -jsi::Function getRemoteFunctionUnpacker(jsi::Runtime &rt) { - auto remoteFunctionUnpacker = rt.global().getProperty(rt, "__remoteFunctionUnpacker"); - react_native_assert(remoteFunctionUnpacker.isObject() && "remoteFunctionUnpacker not found"); - return remoteFunctionUnpacker.asObject(rt).asFunction(rt); -} - -jsi::Object getRemoteFunctionRegistry(jsi::Runtime &rt) { - auto registry = rt.global().getProperty(rt, "__remoteFunctionRegistry"); - react_native_assert(registry.isObject() && "remoteFunctionRegistry not found"); - return registry.getObject(rt); -} - } // namespace jsi::Value makeSerializableClone( @@ -296,61 +285,6 @@ jsi::Value SerializableImport::toJSValue(jsi::Runtime &rt) { return metroRequire.asObject(rt).asFunction(rt).call(rt, source_).asObject(rt).getProperty(rt, imported); } -SerializableRemoteFunction::~SerializableRemoteFunction() { - if (isHostedOnRNRuntime()) { - // TODO: consider batching - const auto &data = std::get(runtimeData_); - data.jsScheduler->scheduleOnJS([id = data.remoteId](jsi::Runtime &rt) { - const auto registry = getRemoteFunctionRegistry(rt); - registry.getPropertyAsFunction(rt, "delete").callWithThis(rt, registry, jsi::Value(id)); - }); - } else { - auto &workletData = std::get(runtimeData_); - cleanupRuntimeAware(hostRuntime_, workletData.function); - } -} - -jsi::Value SerializableRemoteFunction::toJSValue(jsi::Runtime &rt) { - if (&rt == hostRuntime_) { - if (isHostedOnRNRuntime()) { - const auto &rnData = std::get(runtimeData_); - const auto registry = getRemoteFunctionRegistry(rt); - return registry.getPropertyAsFunction(rt, "get").callWithThis(rt, registry, jsi::Value(rnData.remoteId)); - } else { - const auto &workletData = std::get(runtimeData_); - return jsi::Value(rt, *workletData.function); - } - } else { - const auto name = name_.empty() ? jsi::Value::undefined() : jsi::String::createFromUtf8(rt, name_); - auto holderFunction = getRemoteFunctionUnpacker(rt).call(rt, name).getObject(rt); - holderFunction.setNativeState(rt, std::make_shared(shared_from_this())); - return holderFunction; - } -} - -// TODO: generalize it and merge with other scheduling methods -void SerializableRemoteFunction::resolveOrRejectPromise( - const std::shared_ptr &resolveValue, - const std::shared_ptr &runtimeManager) { - if (isHostedOnRNRuntime()) { - const auto &data = std::get(runtimeData_); - data.jsScheduler->scheduleOnJS([resolver = shared_from_this(), resolveValue](jsi::Runtime &rt) { - resolver->toJSValue(rt).getObject(rt).getFunction(rt).call(rt, resolveValue->toJSValue(rt)); - }); - } else { - const auto workletRuntime = runtimeManager->getRuntime(hostRuntimeId_); - // NOLINTNEXTLINE(readability/braces) - if (!workletRuntime) [[unlikely]] { - // Host runtime is dead, most likely we're the last owner of the Remote Function. - // Do nothing. - } else { - workletRuntime->schedule([resolver = shared_from_this(), resolveValue](jsi::Runtime &rt) { - resolver->toJSValue(rt).getObject(rt).getFunction(rt).call(rt, resolveValue->toJSValue(rt)); - }); - } - } -} - jsi::Value SerializableInitializer::toJSValue(jsi::Runtime &rt) { if (remoteValue_ == nullptr) { auto initObj = initializer_->toJSValue(rt); 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 41940974a549..d01a24aeee24 100644 --- a/packages/react-native-worklets/Common/cpp/worklets/SharedItems/Serializable.h +++ b/packages/react-native-worklets/Common/cpp/worklets/SharedItems/Serializable.h @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -278,71 +279,6 @@ class SerializableImport : public Serializable { const std::string imported_; }; -/** Forward declaration */ -class RuntimeManager; - -class SerializableRemoteFunction : public Serializable, - public std::enable_shared_from_this { - private: - struct RNRuntimeData { - const int remoteId; - const std::shared_ptr jsScheduler; - }; - - struct WorkletRuntimeData { - std::unique_ptr function; - }; - - jsi::Runtime *hostRuntime_; - const RuntimeData::RuntimeId hostRuntimeId_; - std::variant runtimeData_; - const std::string name_; - - public: - /** Creates RN Runtime Remote Function. */ - SerializableRemoteFunction( - jsi::Runtime &rnRuntime, - const std::string &name, - const int remoteId, - const std::shared_ptr &jsScheduler) - : Serializable(ValueType::RemoteFunctionType), - hostRuntime_(&rnRuntime), - hostRuntimeId_(RuntimeData::rnRuntimeId), - runtimeData_(RNRuntimeData{.remoteId = remoteId, .jsScheduler = jsScheduler}), - name_(name) {} - - /** Creates Worklet Runtime Remote Function. */ - SerializableRemoteFunction( - jsi::Runtime &workletRuntime, - const std::string &name, - jsi::Function &&function, - RuntimeData::RuntimeId hostRuntimeId) - : Serializable(ValueType::RemoteFunctionType), - hostRuntime_(&workletRuntime), - hostRuntimeId_(hostRuntimeId), - runtimeData_(WorkletRuntimeData{.function = std::make_unique(workletRuntime, std::move(function))}), - name_(name) {} - - ~SerializableRemoteFunction() override; - - SerializableRemoteFunction(const SerializableRemoteFunction &) = delete; - SerializableRemoteFunction &operator=(const SerializableRemoteFunction &) = delete; - - void resolveOrRejectPromise( - const std::shared_ptr &resolveValue, - const std::shared_ptr &runtimeManager); - - jsi::Value toJSValue(jsi::Runtime &rt) override; - - [[nodiscard]] bool isHostedOnRNRuntime() const noexcept { - return std::holds_alternative(runtimeData_); - } - - [[nodiscard]] RuntimeData::RuntimeId getHostRuntimeId() const { - return hostRuntimeId_; - } -}; - class SerializableInitializer : public Serializable { private: // We don't release the initializer since the handle can get diff --git a/packages/react-native-worklets/Common/cpp/worklets/SharedItems/SerializableFactory.cpp b/packages/react-native-worklets/Common/cpp/worklets/SharedItems/SerializableFactory.cpp index 97b078761f93..e912806b3000 100644 --- a/packages/react-native-worklets/Common/cpp/worklets/SharedItems/SerializableFactory.cpp +++ b/packages/react-native-worklets/Common/cpp/worklets/SharedItems/SerializableFactory.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -60,22 +61,22 @@ jsi::Value makeSerializableHostFunction( return SerializableJSRef::newNativeStateObject(rt, serializable); } -jsi::Value makeSerializableRemoteFunction( +jsi::Value makeRNOriginSerializableRemoteFunction( jsi::Runtime &rnRuntime, const std::string &name, const int remoteId, const std::shared_ptr &jsScheduler) { - auto serializable = std::make_shared(rnRuntime, name, remoteId, jsScheduler); + auto serializable = std::make_shared(rnRuntime, name, remoteId, jsScheduler); return SerializableJSRef::newNativeStateObject(rnRuntime, serializable); } -jsi::Value makeSerializableRemoteFunction( +jsi::Value makeWorkletOriginSerializableRemoteFunction( jsi::Runtime &workletRuntime, const std::string &name, jsi::Function &&function, RuntimeData::RuntimeId hostRuntimeId) { - auto serializable = - std::make_shared(workletRuntime, name, std::move(function), hostRuntimeId); + auto serializable = std::make_shared( + workletRuntime, name, std::move(function), hostRuntimeId); return SerializableJSRef::newNativeStateObject(workletRuntime, serializable); } diff --git a/packages/react-native-worklets/Common/cpp/worklets/SharedItems/SerializableFactory.h b/packages/react-native-worklets/Common/cpp/worklets/SharedItems/SerializableFactory.h index a7ce5effb781..7b0229f4832e 100644 --- a/packages/react-native-worklets/Common/cpp/worklets/SharedItems/SerializableFactory.h +++ b/packages/react-native-worklets/Common/cpp/worklets/SharedItems/SerializableFactory.h @@ -59,15 +59,13 @@ jsi::Value makeSerializableHostFunction( const std::string &name, unsigned int paramCount); -/** Creates RN Runtime Remote Function. */ -jsi::Value makeSerializableRemoteFunction( +jsi::Value makeRNOriginSerializableRemoteFunction( jsi::Runtime &rnRuntime, const std::string &name, int remoteId, const std::shared_ptr &jsScheduler); -/** Creates Worklet Runtime Remote Function. */ -jsi::Value makeSerializableRemoteFunction( +jsi::Value makeWorkletOriginSerializableRemoteFunction( jsi::Runtime &workletRuntime, const std::string &name, jsi::Function &&function, diff --git a/packages/react-native-worklets/Common/cpp/worklets/SharedItems/SerializableRemoteFunction.cpp b/packages/react-native-worklets/Common/cpp/worklets/SharedItems/SerializableRemoteFunction.cpp new file mode 100644 index 000000000000..3b1275afe1f6 --- /dev/null +++ b/packages/react-native-worklets/Common/cpp/worklets/SharedItems/SerializableRemoteFunction.cpp @@ -0,0 +1,113 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace facebook; + +namespace worklets { + +namespace { + +jsi::Function getRemoteFunctionUnpacker(jsi::Runtime &rt) { + auto remoteFunctionUnpacker = rt.global().getProperty(rt, "__remoteFunctionUnpacker"); + react_native_assert(remoteFunctionUnpacker.isObject() && "remoteFunctionUnpacker not found"); + return remoteFunctionUnpacker.asObject(rt).asFunction(rt); +} + +jsi::Object getRemoteFunctionRegistry(jsi::Runtime &rt) { + auto registry = rt.global().getProperty(rt, "__remoteFunctionRegistry"); + react_native_assert(registry.isObject() && "remoteFunctionRegistry not found"); + return registry.getObject(rt); +} + +} // namespace + +SerializableRemoteFunction::~SerializableRemoteFunction() = default; + +jsi::Value SerializableRemoteFunction::unpackSelf(jsi::Runtime &rt) { + const auto nameValue = name_.empty() ? jsi::Value::undefined() : jsi::String::createFromUtf8(rt, name_); + auto holderFunction = getRemoteFunctionUnpacker(rt).call(rt, nameValue).getObject(rt); + holderFunction.setNativeState(rt, std::make_shared(shared_from_this())); + return holderFunction; +} + +jsi::Value SerializableRemoteFunction::RNOrigin::toJSValue(jsi::Runtime &rt) { + if (&rt == hostRuntime_) { + const auto registry = getRemoteFunctionRegistry(rt); + return registry.getPropertyAsFunction(rt, "get").callWithThis(rt, registry, jsi::Value(remoteId_)); + } + std::shared_ptr proxy; + { + std::lock_guard lock(proxyMutex_); + proxy = proxy_.lock(); + if (!proxy) { + auto self = std::static_pointer_cast(shared_from_this()); + proxy = std::make_shared(self); + proxy_ = proxy; + } + } + return proxy->toJSValue(rt); +} + +void SerializableRemoteFunction::RNOrigin::resolveOrRejectPromise( + const std::shared_ptr &resolveValue, + const std::shared_ptr & /*runtimeManager*/) { + jsScheduler_->scheduleOnJS([resolver = shared_from_this(), resolveValue](jsi::Runtime &rt) { + resolver->toJSValue(rt).getObject(rt).getFunction(rt).call(rt, resolveValue->toJSValue(rt)); + }); +} + +SerializableRemoteFunction::WorkletOrigin::~WorkletOrigin() { + cleanupRuntimeAware(hostRuntime_, function_); +} + +jsi::Value SerializableRemoteFunction::WorkletOrigin::toJSValue(jsi::Runtime &rt) { + if (&rt == hostRuntime_) { + return jsi::Value(rt, *function_); + } + return unpackSelf(rt); +} + +void SerializableRemoteFunction::WorkletOrigin::resolveOrRejectPromise( + const std::shared_ptr &resolveValue, + const std::shared_ptr &runtimeManager) { + const auto workletRuntime = runtimeManager->getRuntime(hostRuntimeId_); + // NOLINTNEXTLINE(readability/braces) + if (!workletRuntime) [[unlikely]] { + return; + } + workletRuntime->schedule([resolver = shared_from_this(), resolveValue](jsi::Runtime &rt) { + resolver->toJSValue(rt).getObject(rt).getFunction(rt).call(rt, resolveValue->toJSValue(rt)); + }); +} + +SerializableRemoteFunction::RNOrigin::RNOriginProxy::RNOriginProxy(const std::shared_ptr &origin) + : SerializableRemoteFunction(nullptr, origin->getHostRuntimeId(), origin->getName()), origin_(origin) {} + +SerializableRemoteFunction::RNOrigin::RNOriginProxy::~RNOriginProxy() { + origin_->getJSScheduler()->scheduleOnJS([id = origin_->getRemoteId()](jsi::Runtime &rt) { + const auto registry = getRemoteFunctionRegistry(rt); + registry.getPropertyAsFunction(rt, "delete").callWithThis(rt, registry, jsi::Value(id)); + }); +} + +jsi::Value SerializableRemoteFunction::RNOrigin::RNOriginProxy::toJSValue(jsi::Runtime &rt) { + if (&rt == origin_->getHostRuntime()) { + return origin_->toJSValue(rt); + } + return unpackSelf(rt); +} + +void SerializableRemoteFunction::RNOrigin::RNOriginProxy::resolveOrRejectPromise( + const std::shared_ptr &resolveValue, + const std::shared_ptr &runtimeManager) { + origin_->resolveOrRejectPromise(resolveValue, runtimeManager); +} + +} // namespace worklets diff --git a/packages/react-native-worklets/Common/cpp/worklets/SharedItems/SerializableRemoteFunction.h b/packages/react-native-worklets/Common/cpp/worklets/SharedItems/SerializableRemoteFunction.h new file mode 100644 index 000000000000..24761ce7a8f1 --- /dev/null +++ b/packages/react-native-worklets/Common/cpp/worklets/SharedItems/SerializableRemoteFunction.h @@ -0,0 +1,148 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace worklets { + +class RuntimeManager; + +class SerializableRemoteFunction : public Serializable, + public std::enable_shared_from_this { + public: + class RNOrigin; + class WorkletOrigin; + + ~SerializableRemoteFunction() override; + + SerializableRemoteFunction(const SerializableRemoteFunction &) = delete; + SerializableRemoteFunction &operator=(const SerializableRemoteFunction &) = delete; + + facebook::jsi::Value toJSValue(facebook::jsi::Runtime &rt) override = 0; + + virtual void resolveOrRejectPromise( + const std::shared_ptr &resolveValue, + const std::shared_ptr &runtimeManager) = 0; + + [[nodiscard]] virtual bool isHostedOnRNRuntime() const noexcept = 0; + + [[nodiscard]] RuntimeData::RuntimeId getHostRuntimeId() const { + return hostRuntimeId_; + } + + [[nodiscard]] facebook::jsi::Runtime *getHostRuntime() const { + return hostRuntime_; + } + + [[nodiscard]] const std::string &getName() const { + return name_; + } + + protected: + facebook::jsi::Runtime *hostRuntime_; + const RuntimeData::RuntimeId hostRuntimeId_; + const std::string name_; + + SerializableRemoteFunction( + facebook::jsi::Runtime *hostRuntime, + RuntimeData::RuntimeId hostRuntimeId, + const std::string &name) + : Serializable(ValueType::RemoteFunctionType), + hostRuntime_(hostRuntime), + hostRuntimeId_(hostRuntimeId), + name_(name) {} + + facebook::jsi::Value unpackSelf(facebook::jsi::Runtime &rt); +}; + +class SerializableRemoteFunction::RNOrigin final : public SerializableRemoteFunction { + public: + RNOrigin( + facebook::jsi::Runtime &rnRuntime, + const std::string &name, + int remoteId, + const std::shared_ptr &jsScheduler) + : SerializableRemoteFunction(&rnRuntime, RuntimeData::rnRuntimeId, name), + remoteId_(remoteId), + jsScheduler_(jsScheduler) {} + + facebook::jsi::Value toJSValue(facebook::jsi::Runtime &rt) override; + + void resolveOrRejectPromise( + const std::shared_ptr &resolveValue, + const std::shared_ptr &runtimeManager) override; + + [[nodiscard]] bool isHostedOnRNRuntime() const noexcept override { + return true; + } + + [[nodiscard]] int getRemoteId() const { + return remoteId_; + } + [[nodiscard]] const std::shared_ptr &getJSScheduler() const { + return jsScheduler_; + } + + private: + class RNOriginProxy; + + const int remoteId_; + const std::shared_ptr jsScheduler_; + std::weak_ptr proxy_; + std::mutex proxyMutex_; +}; + +class SerializableRemoteFunction::WorkletOrigin final : public SerializableRemoteFunction { + public: + WorkletOrigin( + facebook::jsi::Runtime &workletRuntime, + const std::string &name, + facebook::jsi::Function &&function, + RuntimeData::RuntimeId hostRuntimeId) + : SerializableRemoteFunction(&workletRuntime, hostRuntimeId, name), + function_(std::make_unique(workletRuntime, std::move(function))) {} + + ~WorkletOrigin() override; + + facebook::jsi::Value toJSValue(facebook::jsi::Runtime &rt) override; + + void resolveOrRejectPromise( + const std::shared_ptr &resolveValue, + const std::shared_ptr &runtimeManager) override; + + [[nodiscard]] bool isHostedOnRNRuntime() const noexcept override { + return false; + } + + private: + std::unique_ptr function_; +}; + +class SerializableRemoteFunction::RNOrigin::RNOriginProxy final : public SerializableRemoteFunction { + public: + explicit RNOriginProxy(const std::shared_ptr &origin); + + ~RNOriginProxy() override; + + facebook::jsi::Value toJSValue(facebook::jsi::Runtime &rt) override; + + void resolveOrRejectPromise( + const std::shared_ptr &resolveValue, + const std::shared_ptr &runtimeManager) override; + + [[nodiscard]] bool isHostedOnRNRuntime() const noexcept override { + return false; + } + + private: + std::shared_ptr origin_; +}; + +} // namespace worklets