Skip to content

Feat: Compiled loops and calls run CPython's eval breaker (#141 phase 1) - #207

Merged
b-re-w merged 5 commits into
developfrom
feat/tp-eval-breaker
Oct 17, 2026
Merged

b-re-w merged 5 commits into
developfrom
feat/tp-eval-breaker

Conversation

@b-re-w

@b-re-w b-re-w commented Oct 17, 2026

Copy link
Copy Markdown
Member

Closes #141 (phase 1; phase 2 is folded into #159; the open decision is #152).

Defect fixed: compiled loops and recursion never ran the eval breaker. KeyboardInterrupt did not reach a compiled loop, other threads starved, and on free-threaded builds a stop-the-world request would wait for the loop to end.

Feature added:

  • Every loop iteration and every impl entry polls. Loops use a register countdown (tp_pc_tick), entries the per-thread counter.
  • Every TP_POLL_INTERVAL polls the out-of-line, cold tp_poll_slow calls a module-held lambda: None. Its RESUME runs CPython's own eval-breaker handling: signals, pending calls, forced GIL switching, async exceptions and scheduled GC, exactly as between bytecodes. Public API only.
  • A function holding an array copy-in or an entry-globals snapshot never polls, and defers polls while it is active, because the user code run by a poll could change what it copied.

Tests added: test_eval_breaker.py.

  • Red before the fix: 3 of 4 failed (interrupt in a loop, interrupt in recursion, a second thread runs). The deferral test passed already.
  • Mutation: with the deferral removed, the handler runs mid-call and its write is lost, so the test fails.

Measured cost (quiet window, load 2 to 4, interleaved against develop, 11 pairs; raw data in benchmarks/typedpython/results/2026-10-17-eval-breaker-*.json):

after / before, median range
fannkuch-redux 1.082 (+8.2%) 1.043 to 1.191
fannkuch-redux, loop polls removed (experiment, not shipped) 1.000 0.994 to 1.099
nbody 0.995 0.882 to 1.043

The whole remaining cost is the per-iteration countdown in short inner loops; CPython pays the same kind of check on every back-edge. Accepted by the leader as the price of the fix. Chunked polling for range loops is #206.

Verification (local): full TypedPython suite on CPython 3.14.7: 966 passed, 3 skipped.

@b-re-w

b-re-w commented Oct 17, 2026

Copy link
Copy Markdown
Member Author

Re-measured after a report that another session ran a model test for about 60 s while the MEASURING flag was set earlier today. Both re-runs started in a quiet window (5-minute load at most 3, no torchnative gate running), interleaved, 11 pairs:

first run (16:29 / 16:32) re-run (16:42 / 16:45)
fannkuch-redux, after / before 1.082 (1.043 to 1.191) 1.080 (0.835 to 1.138)
nbody, after / before 0.995 1.001
fannkuch-redux, loop polls removed 1.000 1.007

The figures agree, so the recorded numbers stand.

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.

1 participant