diff --git a/packages/skia/cpp/jsi/JsiPromises.cpp b/packages/skia/cpp/jsi/JsiPromises.cpp index d5927926ff..5e3cbe645c 100644 --- a/packages/skia/cpp/jsi/JsiPromises.cpp +++ b/packages/skia/cpp/jsi/JsiPromises.cpp @@ -1,16 +1,64 @@ #include "JsiPromises.h" +#ifndef NDEBUG +#include "utils/RNSkLog.h" +#endif + namespace RNJsi { JsiPromises::Promise::Promise(jsi::Runtime &rt, jsi::Function resolve, jsi::Function reject) - : runtime_(rt), resolve_(std::move(resolve)), reject_(std::move(reject)) {} + : runtime_(rt), resolve_(std::move(resolve)), reject_(std::move(reject)) { + RuntimeLifecycleMonitor::addListener(rt, this); +} + +JsiPromises::Promise::~Promise() { + bool shouldRemove = false; + { + std::lock_guard lock(mutex_); + shouldRemove = !runtimeDestroyed_; + } + // Call removeListener outside the lock to avoid holding mutex_ across + // an external call. This keeps the locking hierarchy simple and avoids + // potential issues if RuntimeLifecycleMonitor's internals change. + if (shouldRemove) { + RuntimeLifecycleMonitor::removeListener(runtime_, this); + } +} + +void JsiPromises::Promise::onRuntimeDestroyed(jsi::Runtime *rt) { + if (rt != &runtime_) { + return; + } + std::lock_guard lock(mutex_); + runtimeDestroyed_ = true; + { + jsi::Function r(std::move(resolve_)); + jsi::Function j(std::move(reject_)); + } +} void JsiPromises::Promise::resolve(const jsi::Value &result) { + std::lock_guard lock(mutex_); + if (runtimeDestroyed_) { +#ifndef NDEBUG + RNSkia::RNSkLogger::logToConsole( + "Promise::resolve() dropped — runtime already torn down"); +#endif + return; + } resolve_.call(runtime_, result); } void JsiPromises::Promise::reject(const std::string &message) { + std::lock_guard lock(mutex_); + if (runtimeDestroyed_) { +#ifndef NDEBUG + RNSkia::RNSkLogger::logToConsole( + "Promise::reject() dropped — runtime already torn down"); +#endif + return; + } jsi::Object error(runtime_); error.setProperty(runtime_, "message", jsi::String::createFromUtf8(runtime_, message)); diff --git a/packages/skia/cpp/jsi/JsiPromises.h b/packages/skia/cpp/jsi/JsiPromises.h index aa3c07e82a..074d5bae59 100644 --- a/packages/skia/cpp/jsi/JsiPromises.h +++ b/packages/skia/cpp/jsi/JsiPromises.h @@ -1,11 +1,13 @@ #pragma once #include +#include #include #include #include +#include "RuntimeLifecycleMonitor.h" #include "third_party/base64.h" namespace RNJsi { @@ -29,15 +31,20 @@ class LongLivedObject { class JsiPromises { public: - struct Promise : public LongLivedObject { + struct Promise : public LongLivedObject, public RuntimeLifecycleListener { Promise(jsi::Runtime &rt, jsi::Function resolve, jsi::Function reject); + ~Promise(); + + void onRuntimeDestroyed(jsi::Runtime *) override; void resolve(const jsi::Value &result); void reject(const std::string &error); jsi::Runtime &runtime_; + std::mutex mutex_; jsi::Function resolve_; jsi::Function reject_; + bool runtimeDestroyed_{false}; }; using PromiseSetupFunctionType =