Skip to content

Skip checkDebug()'s Python round-trip entirely in fast-continue mode - #52

Merged
revarbat merged 1 commit into
mainfrom
fast-continue-hook-skip
Jul 30, 2026
Merged

Skip checkDebug()'s Python round-trip entirely in fast-continue mode#52
revarbat merged 1 commit into
mainfrom
fast-continue-hook-skip

Conversation

@revarbat

Copy link
Copy Markdown
Member

Summary

  • Found via a real BelfrySCAD bug report: debug + immediate Continue on a NURBS-surface script took ~2x longer than a plain render, even with per-function fast-continue already engaged.
  • Measured root cause: 462,094 checkDebug() calls still crossed into Python even with fast-continue fully applied, because module/geometry evaluation never compiles (a separate, always-interpreted system) — every statement there still called into Python just to be told "continue". Disabling fast-continue entirely ballooned this to 5.99 million calls, confirming the existing feature was helping, just not eliminating the cost.
  • Extends the same fast-continue mechanism to checkDebug() itself, not just VM eligibility: setFastContinueBreakpoints gains a second hookSkippable parameter — true only for a plain "Continue" with no step pending. step_over/step_out keep hookSkippable=false even though they also pass a real breakpoints set, since they still need every statement inspected for their own step_hit logic.
  • Adds FastContinueSignal: a small, lock-free, GIL-free shared flag, since debug_evaluate() runs as one single blocking call with the GIL released for its duration — there's no live Python-callable Evaluator handle pause()/set_breakpoints() could otherwise interrupt directly. A caller creates one per debug session and calls .request() to guarantee the very next checkDebug() call isn't skipped.
  • breakpoint() (forced=true) always bypasses the skip, unaffected.

Test plan

  • New tests in test_debug_hooks.cpp: skip with no matching breakpoint, still-fires at a matching one, unaffected when not hook-skippable (step_over/out), interrupt flag forcing a call through, breakpoint()'s own bypass.
  • Full suite: 1297/1297 passing.
  • test_python_bindings.py: 13/13 passing, unmodified (new params have defaults).
  • Verified end-to-end via the real Python binding: 20,007 → 1 hook call for a representative script with hook_skippable=True and no breakpoints; unaffected (20,007) with hook_skippable=False.
  • Verified the interrupt flag: pre-arming it before a run starts forces exactly one extra hook call through, then correctly self-clears (test-and-clear).

🤖 Generated with Claude Code

Found via a real BelfrySCAD bug report: debugging a NURBS-surface
script and immediately clicking Continue took ~2x longer than a plain
render, even with per-function fast-continue (existing feature)
already engaged. Measured root cause: 462,094 checkDebug() calls still
crossed into Python even with fast-continue fully applied -- module/
geometry evaluation never compiles (a deliberate, separate system, see
docs elsewhere), so every statement there still called into Python
just to be told "continue". Disabling fast-continue entirely made this
5.99 million calls -- confirming those calls WERE genuinely being
helped, just not eliminated.

Extends the same mechanism to checkDebug() itself, not just VM
eligibility: setFastContinueBreakpoints gains a second `hookSkippable`
parameter (true only for a plain "Continue" with no step pending --
step_over/step_out still need every statement inspected for their own
step_hit logic, so they keep hookSkippable=false even though they also
pass a real breakpoints set). checkDebug() then skips the whole Python
call (and the childStatementPositions/getFrame setup before it) for
any line with no breakpoint, in that mode.

Since debug_evaluate() runs as one single blocking call with the GIL
released for its duration, there's no live Python-callable Evaluator
handle DebugSession.pause()/set_breakpoints() (main thread) could
otherwise interrupt directly. Adds FastContinueSignal: a small,
lock-free, GIL-free shared flag a caller creates once per debug
session and calls .request() on to guarantee the very next checkDebug()
call isn't skipped, regardless of whether it hits a breakpoint --
checkDebug() test-and-clears it atomically. breakpoint() (forced=true)
always bypasses the skip, unaffected.

New tests in test_debug_hooks.cpp cover: skip with no matching
breakpoint, still-fires at a matching one, unaffected when not
hook-skippable (step_over/out), the interrupt flag forcing a call
through, and breakpoint()'s own bypass. Full 1297-test suite passes;
verified end-to-end via the actual Python binding (20,007 -> 1 hook
call for a representative script).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@revarbat
revarbat merged commit fddbe22 into main Jul 30, 2026
3 checks passed
@revarbat
revarbat deleted the fast-continue-hook-skip branch July 30, 2026 09:14
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