GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | ||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | ||||||||||||||||||||||
GeezerVison is showing github.com — Fix debug-mode segfault when the asyncgen finalizer hook runs during frame teardown by david-runlayer · Pull Request #756 · MagicStack/uvloop Conversation
|
| path | stock | patched | delta |
|---|---|---|---|
call_soon + cancel, non-debug |
0.125 µs | 0.129 µs | noise (≤4 ns, sign flips between sessions) |
| asyncgen create→finalize, non-debug | 1.491 µs | 1.631 µs | +140 ns (three thread-local ops in the hook) |
call_soon + cancel, debug |
3.337 µs | 3.376 µs | ~1% (within noise) |
| asyncgen create→finalize, debug | 11.836 µs | 7.368 µs | −38% (skips the now-pointless capture) |
Non-debug hot paths are untouched by construction (extract_stack is only reachable under loop._debug); the only non-debug cost is the thread-local save/set/restore per GC'd async generator, on a path whose real cost is dominated by uv_async_send plus the subsequently scheduled create_task/aclose().
In debug mode, the handle scheduling agen.aclose() from the finalizer hook no longer carries _source_traceback. Previously it captured the stack of whichever code happened to be running when GC fired — misleading rather than useful. Task-level debug tracebacks (create_task runs later in normal loop context) and firstiter capture are unaffected.
Validated with the reproducer plus targeted checks (normal handles still get _source_traceback in debug mode; asyncgen finalizer path executes cleanly); relying on CI for the full test matrix.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
CI note: the |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
474e3dd to
3f175ad
Compare
…frame teardown The asyncgen finalizer hook can be invoked by a dealloc while CPython is mid-way through clearing the current interpreter frame (_PyEval_FrameClearAndPop -> PyObject_CallFinalizerFromDealloc -> _asyncgen_finalizer_hook). In debug mode the hook's call_soon_threadsafe creates a Handle whose source-traceback capture calls sys._getframe(), which dereferences the half-cleared frame (garbage frame_obj) and segfaults the interpreter. Observed in production on CPython 3.13.14 under streaming HTTP load (starlette request.stream() async generators being GC'd under cancellation churn): worker SIGSEGV within seconds of enabling loop.set_debug(True). Reproduces on 3.13 and 3.14, x86_64 and aarch64. Fix: skip source-traceback capture (per-thread flag) for handles created from the finalizer hook -- the traceback of a GC point is meaningless for debugging anyway -- and make extract_stack() tolerate AttributeError/ ValueError/TypeError from walk_stack, which py3.14 can raise when non-frame objects (e.g. _asyncio.TaskStepMethWrapper) appear in the stack (MagicStack#715).
3f175ad to
1a15864
Compare
|
I also ran into this issue in |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
`_CachedRequest.wrapped_receive` created a new `Request.stream()` generator on every call, took one chunk and abandoned it suspended, leaving it to be finalized by the GC. `body()` and the form parsers could do the same on error or cancellation. Under uvloop debug mode the asyncgen finalizer hook can segfault when run from GC (MagicStack/uvloop#756), and earlier surfaced as the traceback in Kludex#3066. Wrap every internal use of `self.stream()` in `contextlib.aclosing`, drop the unused `_wrapped_rc_stream`, and remove the pytest filter that was suppressing trio's ResourceWarning for this leak so it can't regress. Co-Authored-By: Claude Opus 5.5 <[email protected]>
There was an error while loading. Please reload this page.
Successfully merging this pull request may close these issues.
There was an error while loading. Please reload this page.