Skip to content

Commit 0ee69de

Browse files
committed
fix(🐛): prevent EXC_BAD_ACCESS in Promise destructor after runtime teardown
When async operations (e.g. fromURI image loads) complete after the JSI runtime has been torn down, the Promise destructor crashes with a null pointer dereference in jsi::Pointer::~Pointer(). This happens because resolve_ and reject_ (jsi::Function members) attempt to release their PointerValue through the now-invalid runtime. The fix adds an explicit destructor that moves the jsi::Function members to the heap before implicit member destruction runs. After std::move, the member's internal ptr_ is null (per jsi::Pointer's move constructor), so implicit destruction safely skips the runtime access. The heap copies are intentionally leaked (~32 bytes per Promise) since there is no safe way to release JSI pointers without a valid runtime.
1 parent c010bb9 commit 0ee69de

2 files changed

Lines changed: 10 additions & 0 deletions

File tree

packages/skia/cpp/jsi/JsiPromises.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,15 @@ JsiPromises::Promise::Promise(jsi::Runtime &rt, jsi::Function resolve,
66
jsi::Function reject)
77
: runtime_(rt), resolve_(std::move(resolve)), reject_(std::move(reject)) {}
88

9+
JsiPromises::Promise::~Promise() {
10+
// Move JSI function pointers to the heap so the member destructors
11+
// see ptr_==nullptr and skip the runtime access. The heap copies are
12+
// intentionally leaked — there is no safe way to release JSI pointers
13+
// when the runtime may already be torn down.
14+
(void)new jsi::Function(std::move(resolve_));
15+
(void)new jsi::Function(std::move(reject_));
16+
}
17+
918
void JsiPromises::Promise::resolve(const jsi::Value &result) {
1019
resolve_.call(runtime_, result);
1120
}

packages/skia/cpp/jsi/JsiPromises.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class JsiPromises {
3131
public:
3232
struct Promise : public LongLivedObject {
3333
Promise(jsi::Runtime &rt, jsi::Function resolve, jsi::Function reject);
34+
~Promise();
3435

3536
void resolve(const jsi::Value &result);
3637
void reject(const std::string &error);

0 commit comments

Comments
 (0)