Skip to content

fix(jit): task code stays interpreted at every JIT entry point (#533)#534

Merged
InauguralPhysicist merged 1 commit into
mainfrom
jit-task-gate
Jul 10, 2026
Merged

fix(jit): task code stays interpreted at every JIT entry point (#533)#534
InauguralPhysicist merged 1 commit into
mainfrom
jit-task-gate

Conversation

@InauguralPhysicist

Copy link
Copy Markdown
Collaborator

Closes #533

The bug

The #408 ruling ("task code runs interpreted") was enforced only at the fresh-entry JIT gate. The OSR back-edge counter/handoff and the OP_CALL / OP_DISPATCH thunk entries weren't task-gated — so a task's hot mailbox loop crossing the OSR threshold (default 5000) got JIT-compiled mid-task. Inside native code, a blocking task_recv runs via jit_helper_call, which has no g_task_suspend_request check: the placeholder null flowed onward as the "received" message, and the leaked flag suspended the task mid-expression at the next interpreted call site.

Symptoms: task_recv returns null, suspended-task locals read as garbage, mass task death right around the OSR threshold. First seen as liferaft's node tasks dying en masse under chaos (#523) — reachable at all only once #530 lifted the 255-spawn lifetime cap (nothing before it could run a task loop hot). Full forensic chain (suspend-flag ledger, __builtin_return_address on the blocking-recv path, 70-line shrink repro) in #533.

The fix

All four entry points now check g_task_sched, mirroring the fresh-entry gate: OSR counter+handoff, both thunk entries, and a belt-and-braces bail in jit_helper_call. Non-task programs keep the JIT exactly as before.

Tests

tests/test_task_osr.eigs + suite wiring with EIGS_JIT_OSR_THRESHOLD=20: a worker recv-loop driven 200 messages past the threshold must receive real strings throughout. Red pre-fix (FAILED got=-1 — placeholder null received), green post-fix.

Validation

  • Full suite, release: 2705/2705
  • Full suite, ASan + detect_leaks=1: 2705/2705, leak tally 0
  • liferaft 5-node × 1500-step chaos (the discovering workload): mass node death → 237/238 committed, invariants clean, applied-streams consistent

🤖 Generated with Claude Code

The #408 ruling — task code runs interpreted — was enforced only at the
fresh-entry gate. Three other entry points weren't task-gated: the OSR
back-edge counter/handoff, and the OP_CALL / OP_DISPATCH thunk entries.
A task's hot loop crossing the OSR threshold was JIT-compiled MID-TASK,
and jit_helper_call has no g_task_suspend_request check: a blocking
task_recv's placeholder null flowed onward as the received message and
the leaked suspend flag suspended the task mid-expression at a later
interpreted call site. Symptom: mass task death, null from task_recv,
corrupted locals after ~OSR-threshold loop iterations — first seen as
liferaft node tasks dying en masse under chaos (#523), reachable at all
only once #530 lifted the lifetime spawn cap.

All four entry points now check g_task_sched (plus a belt-and-braces
bail in jit_helper_call). Regression: tests/test_task_osr.eigs drives a
recv loop past a lowered EIGS_JIT_OSR_THRESHOLD — red pre-fix (worker
receives the placeholder null), green post-fix.

Root-caused with a suspend-flag ledger + __builtin_return_address on
the blocking-recv path; 70-line shrink repro in #533.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@InauguralPhysicist InauguralPhysicist merged commit 15925c9 into main Jul 10, 2026
17 checks passed
@InauguralPhysicist InauguralPhysicist deleted the jit-task-gate branch July 10, 2026 04:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

JIT: OSR compiles task code mid-task — jit_helper_call has no suspend check (state corruption after ~threshold iterations)

1 participant