Skip to content

--gc-aggressive killed every session on a PyPy target, silently - #270

Merged
devdanzin merged 1 commit into
mainfrom
gc-aggressive-kills-every-session-on-pypy
Sep 3, 2026
Merged

--gc-aggressive killed every session on a PyPy target, silently#270
devdanzin merged 1 commit into
mainfrom
gc-aggressive-kills-every-session-on-pypy

Conversation

@devdanzin

Copy link
Copy Markdown
Owner

Found triaging ~/crashers/fusil-fleet-pypy_01, which reported 43 368 sessions, 0 crashes,
0 timeouts, 0 cpu-load kills
. None of those sessions ran.

The bug

gc.set_threshold is CPython-only, and the prelude runs on the target. On PyPy the
unguarded call raises AttributeError at module level — line 55 of every generated script,
before a single target call:

  File "session-391/source.py", line 55, in <module>
    gc.set_threshold(1, 1, 1)
AttributeError: module 'gc' has no attribute 'set_threshold'

The failure mode is the dangerous part: the sessions don't look broken, they look clean.
Score 0.0%, duration 0.3 s, no crash, no timeout, nothing in any log saying why. A fleet can
run for hours reporting a tidy zero.

Verified on a real script from that fleet

output exit
as the fleet ran it 6 lines, AttributeError 1
with the guard 500 lines, --- Fuzzing 40 functions in traceback --- 1

The fix

import gc
try:
    gc.set_threshold(1, 1, 1)
except AttributeError:
    pass  # not CPython: no generational threshold to tighten

A no-op rather than an error on such a target: --gc-aggressive is a tuning knob, not a
request, and PyPy's GC is not generational in CPython's sense, so there is nothing to coerce.

Tests

Two, both resistant to the obvious ways this could regress:

  • one walks the AST to assert the set_threshold call sits inside a try/except AttributeError, rather than matching source text that a reformat could break;
  • one execs the emitted snippet against a gc module with no set_threshold — which is
    exactly what PyPy looks like from the prelude's point of view.

The recurring lesson

This is the same shape as the pre-3.13 target-compat break fixed in #256, where every session
also died in the prelude. The rule that keeps being relearned: anything CPython-specific in
the prelude must be guarded, because the prelude runs on the target.
The faulthandler block
right above already does this and says so in its comment.

1286 tests OK, ruff check and ruff format --check clean.

`gc.set_threshold` is CPython-only. The prelude runs on the TARGET, so on PyPy the
unguarded call raised AttributeError at MODULE LEVEL, at line 55, before a single
target call:

    import gc
    gc.set_threshold(1, 1, 1)
    AttributeError: module 'gc' has no attribute 'set_threshold'

The failure mode is the bad part: the sessions do not look broken, they look CLEAN.
A 43 368-session PyPy fleet ran to completion reporting zero crashes, zero timeouts,
zero cpu-load kills and 0.3-second sessions, and nothing in any log said why. Every
one of those sessions was dead on arrival.

Verified on a real script out of that fleet: before the guard it produces 6 lines of
output and exits 1; after, 500 lines and it is fuzzing (`--- Fuzzing 40 functions in
traceback ---`).

The flag stays a no-op rather than an error on such a target -- it is a tuning knob,
not a request, and PyPy's GC is not generational in CPython's sense so there is
nothing to coerce.

Two tests: one asserts the call is inside a try/except AttributeError by walking the
AST rather than matching text, and one execs the emitted snippet against a gc module
with no set_threshold, which is what PyPy looks like from the prelude's point of view.

Same shape as the pre-3.13 target-compat break fixed in #256. The general rule that
keeps being relearned: anything CPython-specific in the prelude must be guarded,
because the prelude runs on the target.
@devdanzin
devdanzin merged commit 8bbd8b6 into main Sep 3, 2026
2 checks passed
@devdanzin
devdanzin deleted the gc-aggressive-kills-every-session-on-pypy branch September 3, 2026 04:52
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