Repository navigation
Description
Bug report
On a release free-threaded build, ordinary directory operations can raise
BlockingIOError: [Errno 11] Resource temporarily unavailable while other
threads in real subinterpreters collect garbage. A fresh FileFinder for an
existing .py file can fail in the same way. No third-party extension is
needed.
_PyThreadState_Attach() already saves and restores errno, but only under
Py_DEBUG. Making that existing preservation unconditional fixes the same
public regression on current 3.15 and main, without changing the IO functions
or their error checks. The native change removes four preprocessor lines.
The proposed regression runs os.listdir, os.scandir and a newly created
importlib.machinery.FileFinder concurrently with child-local gc.collect().
It creates and closes real concurrent.interpreters children. It does not
replace a loader, prime import caches, retry a failed operation, force the GIL,
or use a native fault injector. A local temporary directory contains a valid
fixture.py and 32 ordinary files throughout the run.
Reproduction
The attached source patch adds
test.test_free_threading.test_os.TestOs.test_directory_io_during_subinterpreter_gc.
Apply only its test-file hunk to the unmodified source, build with
--disable-gil --enable-shared and without --with-pydebug, then run:
./python -I -B -m unittest -v test.test_free_threading.test_os.TestOs.test_directory_io_during_subinterpreter_gcBefore the pystate.c change, the assertion reports real
BlockingIOError(11, 'Resource temporarily unavailable') entries. After
removing the two Py_DEBUG conditionals around the existing save/restore,
the identical test passes. The full standard-library test suites listed below
also pass. These are local native source builds, not a claim about released
binary distributions.
| Official source | Before | After | IO/GC/threading/import JUnit cases |
|---|---|---|---|
3.15 be7dbf98e7d5227f14bc73d8b3f422f60c431d60 |
FAIL, real EAGAIN | PASS | 2100, 0 failures/errors, 163 existing skips |
main 9d4a35c494cb1cfd9c77c619b22bd425695316c2 |
FAIL, real EAGAIN | PASS | 2120, 0 failures/errors, 167 existing skips |
The original suites were test_free_threading.test_os, test_os, test_gc,
test_threading and test_importlib, including their collected submodules.
Linux musl x86_64, release free-threaded builds; Py_DEBUG=0,
Py_GIL_DISABLED=1, no PYTHON_GIL override, sys.flags.gil is None and the
GIL stays off. PYTHON_JIT=1 was requested; these builds have no JIT available.
A separate all-rounds public API probe recorded 20675 failed cases out of
65536 on the original 3.15.2 release core, and zero on the source-corrected
core. The regression above is also independently reproduced on current
official branch sources, so the report does not depend on that older core.
CPython versions tested on
3.15, CPython main branch (3.16)
Operating systems tested on
Linux (native musl x86_64)
Source patch and public regression
Native change and regression test. The change is proposed against main; the source/runtime evidence above includes an independent 3.15 backport.
AI assistance was used to investigate the native lifetime and prepare the patch, regression, and report. All reported before/after results were executed against real native source builds; no simulated results are included.
Linked PRs
Metadata
Metadata
Assignees
Labels
Projects
- StatusShow more project fieldsTodo
Activity
pythongh-161769: Document errno preservation across thread-state atta…
pythongh-161769: Render errno as a literal in NEWS
pythongh-161769: Use standard long timeouts for concurrent regression
pythongh-161769: Focus errno regression on ordinary threads and GC