You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Found while completing #443's pump-termination item (three empirical rounds on the #444 branch); target semantics below are verified on Android, where terminate-during-entry works (android#2021 @ 351bef64, spec-proven with terminate landing 0-1ms into the entry body).
The defect (iOS)
The no-op: WorkerWrapper::workerIsolate_ is assigned only after the background looper's func() returns — and func() is where entry evaluation (the pumped module-graph load, any top-level-await park) runs. Terminate() bails on the null isolate, so for a worker still inside its entry, worker.terminate() does nothing: no TerminateExecution, no termination-requested flag. The pump bails landed in #444 engage only once the worker has a published isolate — the parked-entry scenario #443's pump item describes is unreachable on iOS for this reason.
The wedge: publishing the isolate early (via a separate atomic, drain-guard semantics untouched) makes Terminate() genuinely fire mid-entry — and the suite then hangs past 600s in teardown. That attempt was reverted on #444's branch: shipping it would convert a silent no-op into a hang. The comment at WorkerWrapper.mm (Terminate) records the gap at the site.
Target semantics — what correct terminate-during-entry looks like
Verified end-to-end on Android (trace + spec); the fix here must satisfy all six:
Prompt pump exit via a termination-requested flag, not the V8 probe alone — IsExecutionTerminating is a mid-unwind probe and a parked pump runs no JS. (Already converged: android e11c9c30 / iOS's requested-flag in fix: loader additional hardening (#443) #444; iOS's flag is currently unreachable mid-entry because of the no-op above.)
Termination never masquerades: the evaluator checks termination BEFORE its timeout branch (never "Top-level await timed out"), and exception construction has an explicit HasTerminated/empty-Message branch (never an entry rejection with a fabricated message).
No onerror on a dying worker: the error router early-returns on the wrapper's terminating flag — a terminated entry produces neither worker-scope onerror nor a parent error event. Terminate is not an error.
Everything after the bail runs no JS and tolerates empty answers: the settle-gate's capability re-Evaluate() returns empty on a terminating isolate and must fall through inertly; the teardown chain drops loop entries and releases handles only, never consuming an unchecked Maybe before Dispose.
Error-report building must be termination-safe: anything between the bail and disposal that formats messages/stacks must read through FromMaybe/IsEmpty, never ToChecked/ToLocalChecked — the termination interrupt can materialize inside the reporter itself (ToDetailString runs JS). Android hit exactly this CHECK-abort and fixed it in 351bef64; iOS's exception/message formatters need the same audit as part of this work.
The window is real, not theoretical: on Android the spec's terminate landed 0-1ms into the entry body on every iteration. Any fix must be spec-proven with the same shape (worker .mjs entry parked in TLA, terminated mid-pump, repeated iterations).
Scope
A worker-lifecycle design pass: early isolate publication paired with a teardown path that tolerates termination landing inside entry evaluation (the pumped graph load, the TLA park, and the queue-arm-before-isolate window are all live during func()), plus the rule-5 formatter audit. Cross-runtime contract: the six rules above should hold identically on both platforms; Android already conforms.
Found while completing #443's pump-termination item (three empirical rounds on the #444 branch); target semantics below are verified on Android, where terminate-during-entry works (android#2021 @ 351bef64, spec-proven with terminate landing 0-1ms into the entry body).
The defect (iOS)
The no-op:
WorkerWrapper::workerIsolate_is assigned only after the background looper'sfunc()returns — andfunc()is where entry evaluation (the pumped module-graph load, any top-level-await park) runs.Terminate()bails on the null isolate, so for a worker still inside its entry,worker.terminate()does nothing: noTerminateExecution, no termination-requested flag. The pump bails landed in #444 engage only once the worker has a published isolate — the parked-entry scenario #443's pump item describes is unreachable on iOS for this reason.The wedge: publishing the isolate early (via a separate atomic, drain-guard semantics untouched) makes
Terminate()genuinely fire mid-entry — and the suite then hangs past 600s in teardown. That attempt was reverted on #444's branch: shipping it would convert a silent no-op into a hang. The comment atWorkerWrapper.mm(Terminate) records the gap at the site.Target semantics — what correct terminate-during-entry looks like
Verified end-to-end on Android (trace + spec); the fix here must satisfy all six:
IsExecutionTerminatingis a mid-unwind probe and a parked pump runs no JS. (Already converged: android e11c9c30 / iOS's requested-flag in fix: loader additional hardening (#443) #444; iOS's flag is currently unreachable mid-entry because of the no-op above.)onerrornor a parent error event. Terminate is not an error.Evaluate()returns empty on a terminating isolate and must fall through inertly; the teardown chain drops loop entries and releases handles only, never consuming an unchecked Maybe beforeDispose.FromMaybe/IsEmpty, neverToChecked/ToLocalChecked— the termination interrupt can materialize inside the reporter itself (ToDetailStringruns JS). Android hit exactly this CHECK-abort and fixed it in 351bef64; iOS's exception/message formatters need the same audit as part of this work.Scope
A worker-lifecycle design pass: early isolate publication paired with a teardown path that tolerates termination landing inside entry evaluation (the pumped graph load, the TLA park, and the queue-arm-before-isolate window are all live during
func()), plus the rule-5 formatter audit. Cross-runtime contract: the six rules above should hold identically on both platforms; Android already conforms.