NativeScript / NativeScript/ios

worker.terminate() is a no-op during entry evaluation, and terminating there wedges teardown

Open
#445 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
JavaScript
Stars
150
Forks
43
Avg merge
3d 10h
Merged PRs (30d)
22

Description

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:

  1. 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 #444; iOS's flag is currently unreachable mid-entry because of the no-op above.)
  2. 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).
  3. 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.
  4. 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.
  5. 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.
  6. 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.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at the Terminate implementation and its comment in WorkerWrapper.mm, then compare the iOS behavior with Android's e11c9c30 and 351bef64 changes. Trace entry evaluation, the pumped module graph, TLA parking, settle-gate evaluation, teardown, and exception/message formatting. Done means all six stated termination rules hold on both platforms, with repeated spec tests terminating a parked .mjs entry mid-pump.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
backend
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.