Repository navigation
fix(worker): keep a worker isolate alive while a __runOnMainThread callback locks it - #2068
adrian-niculescu wants to merge 2 commits into
Conversation
…llback locks it RunMainThreadEntry read the target isolate from its entry, released the cache lock and only then took the isolate's Locker. A worker that ended in between removed the entry and disposed the isolate, and the main thread then locked freed memory. The main thread now holds the isolate from the moment it takes the entry until it releases the Locker, and a worker waits for those holds before disposing its isolate.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configuration
📒 Files selected for processing (4)
Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 0 remain after this review. 📝 WalkthroughWalkthroughCallbackHandlers tracks active main-thread callbacks by isolate and provides a wait method. BackgroundLooper waits for callbacks after destroying the worker runtime and before disposing its isolate. Worker callback exception handling releases its JavaScript handle before rethrowing. ChangesWorker callback lifecycle
Priority: ⬇️ Low Estimated code review effort: 2 (Simple) | ~15 minutes Change: Bug fix Sequence Diagram(s)sequenceDiagram
participant BackgroundLooper
participant Runtime
participant CallbackHandlers
participant MainThreadCallback
participant Isolate
BackgroundLooper->>Runtime: DestroyRuntime()
Runtime->>CallbackHandlers: RemoveIsolateEntries()
BackgroundLooper->>CallbackHandlers: WaitForMainThreadCallbacks(isolate)
MainThreadCallback->>CallbackHandlers: RunMainThreadEntry()
CallbackHandlers->>Isolate: Hold isolate while callback runs
CallbackHandlers-->>CallbackHandlers: Release hold after isolate locker
CallbackHandlers-->>BackgroundLooper: Return when no holds remain
BackgroundLooper->>Isolate: Dispose()
Suggested reviewers: Merge Risk: ⚪ Minimal · up to No merge-blocking issue is established; the worker callback shutdown change is ready for normal merge checks. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. A rabbit counts each callback’s stay Comment |
…s isolate handle The exception a worker's callback threw kept a handle into the worker isolate after RunMainThreadEntry let go of it, so the isolate could be disposed before the event loop reported it, and the report resolved the handle against the main thread's isolate. For a worker's callback the exception now drops the handle while the isolate is still held, and only its message and stack reach Java.
A worker that calls
__runOnMainThread(fn)and ends shortly after, for example throughclose(), can crash the app: the main thread locks the worker's isolate after the worker has disposed it, and dies with a SIGSEGV inv8::Locker::Initialize.CallbackHandlers::RunMainThreadEntryreads the isolate from its cache entry, releasescacheMutex_, and only then constructs thev8::Locker. Worker teardown removes the entry inDestroyRuntimeand disposes the isolate right after, so an entry the main thread already took can outlive its isolate. The main thread now records a hold on the isolate in the samecacheMutex_section where it reads the entry, and drops it only after itsLockeris released. The worker waits for those holds after it has released its ownLockerand before it disposes the isolate. Waiting with the worker'sLockerstill held would deadlock against the main thread'sLocker. A callback that took the entry beforeRemoveIsolateEntriesran then finds the entry gone under a live isolate and returns, and one that did not take it never sees the isolate.A callback that throws has the same problem in another form: the exception kept a handle into the worker isolate after the hold was gone, and the event loop resolved that handle against the main thread's isolate when it reported it. For a worker's callback the exception now drops the handle while the isolate is still held, so the report carries its message and stack, as a failed runtime initialization already does. A callback on the main isolate keeps the handle, so its report is unchanged.
The window is a few instructions wide, so there is no spec for it. With a sleep injected between reading the entry and taking the
Locker, a worker that closes right after posting its callback crashes the main thread onmainand does not with this change. The full device suite passes.Summary by CodeRabbit