Skip to content

fix: Schedule deletion only if object was GC'd - allow dispose() fast path#3705

Merged
wcandillon merged 2 commits intoShopify:mainfrom
mrousavy:fix/dispose-direct-vs-hades-gc
Feb 12, 2026
Merged

fix: Schedule deletion only if object was GC'd - allow dispose() fast path#3705
wcandillon merged 2 commits intoShopify:mainfrom
mrousavy:fix/dispose-direct-vs-hades-gc

Conversation

@mrousavy
Copy link
Contributor

This is a follow-up PR to #3704 - this implements point 1 in my Update comment.

It changes the deletion logic of Thread-confined resources (that's SkImage, SkPicture and SkSurface) to only schedule the call on a different Thread if the object is being GC'd, which is in Hermes happening on a different Thread.

This allows dispose() to dispose immediately instead of also scheduling deletion like it did previously.

So in other words; dispose() releases the object now (as we are on the same Thread), and if an object is passively being deleted by the Hermes GC, it's deletion is scheduled on a different Thread.

@mrousavy
Copy link
Contributor Author

mrousavy commented Feb 11, 2026

FWIW, we should use an actual Dispatcher to schedule the deletion, not a Queue.

The implementation (both before and after my PR) is prone to stale objects, e.g. if you create an SkImage, and never create another SkImage again - the last SkImage might still float in memory as the Dispatcher::processQueue() is never called, hence it never being deleted.

A solution (as a follow up PR) would be to use a real Dispatcher. Unfortunately there's no Dispatcher API available in JS environments - you can realistically only get a JS Thread Dispatcher from React Native, a UI Thread Dispatcher from the host platform, but then if it's a separate Thread (e.g. createWorkletRuntime(...), or VisionCamera Frame Processor) there's no Dispatcher you can use.
In Nitro there is a Dispatcher you can use for every Runtime, but Skia does not use Nitro.

@mrousavy
Copy link
Contributor Author

We do need to add tests for this PR though, maybe it'd be a good idea to somehow test the VisionCamera + Skia integration on both our ends (e.g. CI tests) to make sure it's;

  1. Working (not errors thrown)
  2. Rendering actual content (no black screens)
  3. Working stable over a few seconds (no memory leaks, no stalls)

I'll figure out how I can do this on my end in VisionCamera

@wcandillon
Copy link
Contributor

This also makes sense to me 👍 Even if we were to keep the Dispatcher, we need to allow of immediate dispose of GPU bound resources. So potentially we could even merge this PR as an intermediary step.

Copy link
Contributor

@wcandillon wcandillon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great thank you 🙏

@wcandillon
Copy link
Contributor

@mrousavy once we merge this we can also probably merge #3704 too depending on how the testing goes

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates destruction semantics for thread-confined Skia JSI host objects so that dispose() can release the wrapped sk_sp<> immediately, while GC-driven finalization still defers deletion onto the object’s creation thread via Dispatcher.

Changes:

  • Remove the releaseResources() overrides that always deferred deletion via Dispatcher.
  • Update destructors for JsiSkImage, JsiSkPicture, and JsiSkSurface to only enqueue deferred deletion when the object wasn’t explicitly disposed.
  • Keep Dispatcher::processQueue() calls in constructors (with a clarifying comment in JsiSkImage).

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
packages/skia/cpp/api/JsiSkSurface.h Make GC finalization schedule surface deletion; allow dispose() to release immediately.
packages/skia/cpp/api/JsiSkPicture.h Same pattern applied to SkPicture.
packages/skia/cpp/api/JsiSkImage.h Same pattern applied to SkImage, plus a comment clarifying queue processing.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@wcandillon
Copy link
Contributor

@mrousavy I appear to have picked up a small regression using the video example with this PR, looking into it now.

@wcandillon wcandillon merged commit 2727f13 into Shopify:main Feb 12, 2026
31 of 32 checks passed
@github-actions
Copy link
Contributor

🎉 This PR is included in version 2.4.20 🎉

The release is available on:

Your semantic-release bot 📦🚀

@mrousavy
Copy link
Contributor Author

Hey what was the regression btw? Is this fixed?

@wcandillon
Copy link
Contributor

yes it was a false alarm

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants