Repository navigation
fix(worker): drop callbacks from closing workers - #2108
edusperoni wants to merge 1 commit into
Conversation
|
No actionable comments were generated in the recent review. Walkthrough
Merge Risk:
|
The main thread read a worker's isolate from the callback cache, released the cache lock, then took a Locker on it. A worker shutting down in that window disposed the isolate first, and the main thread crashed in v8::Locker::Initialize. Each worker now has a gate the main thread holds from checking it until it owns the worker's Locker. Shutdown closes the gate before taking its teardown Locker, so a callback that arrives after that is dropped without touching the isolate, and the worker never waits on the main thread. A worker callback that throws under uncaughtErrorPolicy "throw" is now reported through the worker's own error path (its onerror, then the parent's Worker object) instead of being rethrown on the main thread, where it became a Java exception that resolved a handle from the worker isolate against the main one and crashed.
aebaba9 to
72e46c6
Compare
Description
Supersedes #2068 with a different approach to the same two crashes.
Race on worker shutdown.
RunMainThreadEntryread a worker's isolate from the callback cache, releasedcacheMutex_, and only then took av8::Lockeron it. A worker shutting down in that window (DestroyRuntime→Dispose) freed the isolate first, and the main thread crashed inv8::Locker::Initialize.#2068 fixed this by making the worker wait for in-flight main-thread holds before disposing. That keeps the isolate alive for a caller that will then only find its entry gone: the main thread still locks and enters a worker that was told to terminate, and teardown waits on main-thread progress. This PR drops the call instead:
IsolateGate(std::mutex+open), held byshared_ptrin the cache entry so it outlives the isolate.Locker.Locker, while holding noLocker. Once closed, the main thread never touches the isolate. The worker never waits for the main thread beyond theLockercontention that already existed.Locker, so there is no lock-order cycle. A global mutex would deadlock against a worker that calls__runOnMainThreadwhile holding its ownLocker.Main-isolate entries (multithreaded JS) carry no gate and are unchanged.
Throwing worker callbacks. Under
uncaughtErrorPolicy: "throw", a worker callback's uncaught error was rethrown on the main thread as a Java exception. That exception resolved a handle from the worker isolate against the main one and crashed (SIGSEGVinNativeScriptException::ReThrowToJava). Worse, it would have taken the app down for an error that belongs to the worker. It now goes through the worker's own error path (CallWorkerScopeOnErrorHandle: the worker'sonerror, then the parent'sWorkerobject), as the worker's message dispatch already does. Under the defaultreportpolicy nothing changes.Does your commit message include the wording below to reference a specific issue in this repo?
No issue; supersedes #2068.
Related Pull Requests
Does your pull request have unit tests?
No new spec. The race window is a few instructions wide, and the
throwpolicy can't run in the device suite because it crashes the app on purpose (seetestUncaughtErrorPolicy.js). Verified manually with a focused repro on an arm64 API 35 emulator, withusleep(200000)injected inRunMainThreadEntryright after the entry lookup:main__runOnMainThread(cb), busy-waits 50 ms, thenclose()(x5)SIGSEGVinv8::Locker::InitializecbdroppeduncaughtErrorPolicy: "throw", worker's__runOnMainThreadcallback throwsSIGSEGVinNativeScriptException::ReThrowToJavaWorker.onerrorgetsUncaught Error: boom-from-worker-callback, app aliveThe full device suite passes (
runtestsAndVerifyResults: 1545/1545, on top of #2067, #2069 and #2070).Summary by CodeRabbit