Commit 7a03f7c
authored
fix(python): keep private-loop worker off Python during interpreter exit (#2008)
## What
Fixes the remaining red on main after #2007: the `python.yml`
**Examples** job crashed with SIGABRT (core dumped) in
`langgraph_async_tool.py` at process exit — flaky, ~25% of runs (it
passed on the #2007 PR run, failed on the main push run).
## Why
Core-dump analysis: the `bashkit-py-loop` private-loop worker thread
wakes from `recv()` when the callback engine is gc'd — which commonly
happens inside `Py_Finalize`'s GC pass — and called `Python::attach` to
close its asyncio event loop. Attaching a fresh thread state during
interpreter finalization fatals CPython:
```
Fatal Python error: PyGILState_Release: thread state ... must be current when releasing
Python runtime state: finalizing
```
`Python::try_attach` was tried first and does not help: its finalization
check is compiled only for Python ≥ 3.13, and `Py_IsInitialized()` still
returns 1 during `Py_FinalizeEx`'s GC on older versions (confirmed with
a second core dump showing the abort inside `try_attach` itself).
This race predates #2007 — it shipped with the private-loop worker
redesign (#1918) — but was masked because every `python.yml` run on main
since June 6 was cancelled by subsequent pushes.
## How
The worker's exit path no longer touches Python at all: the loop's `Py`
ref is dropped unattached (pyo3 safely defers the decref) and the loop
is closed by asyncio's `BaseEventLoop.__del__` when the deferred decref
runs, or reclaimed by the OS at process exit. Documented as TM-PY-030
variant (3) in `specs/threat-model.md`.
## Tests
- Before: `langgraph_async_tool.py` aborted 6/30 runs (and 2/5, 1/10 in
other rounds). After: **0/80 across two 40-run stress rounds**.
- Full bashkit-python pytest suite: 700 passed, 1 skipped.
- `cargo fmt --check` / `cargo clippy -p bashkit-python --all-targets`
clean.
- The Examples CI job itself is the ongoing regression signal for this
exit-time race (it runs the crashing example on every PR).
---
_Generated by [Claude
Code](https://claude.ai/code/session_01DtAsttyBKbB8jQFwzxTw1F)_1 parent 61080c6 commit 7a03f7c
2 files changed
Lines changed: 20 additions & 6 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2249 | 2249 | | |
2250 | 2250 | | |
2251 | 2251 | | |
2252 | | - | |
2253 | | - | |
2254 | | - | |
| 2252 | + | |
| 2253 | + | |
| 2254 | + | |
| 2255 | + | |
| 2256 | + | |
| 2257 | + | |
| 2258 | + | |
| 2259 | + | |
| 2260 | + | |
| 2261 | + | |
2255 | 2262 | | |
2256 | 2263 | | |
2257 | 2264 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1745 | 1745 | | |
1746 | 1746 | | |
1747 | 1747 | | |
1748 | | - | |
| 1748 | + | |
1749 | 1749 | | |
1750 | 1750 | | |
1751 | 1751 | | |
| |||
1767 | 1767 | | |
1768 | 1768 | | |
1769 | 1769 | | |
1770 | | - | |
| 1770 | + | |
| 1771 | + | |
| 1772 | + | |
| 1773 | + | |
| 1774 | + | |
| 1775 | + | |
| 1776 | + | |
1771 | 1777 | | |
1772 | | - | |
| 1778 | + | |
| 1779 | + | |
1773 | 1780 | | |
1774 | 1781 | | |
1775 | 1782 | | |
| |||
0 commit comments