Closed
Description
Running this script under free-threading build:
import os
import tempfile
import threading
N=2
COUNT=100
def writer(file, barrier):
barrier.wait()
for _ in range(COUNT):
f.write("x")
def reader(file, stopping):
while not stopping.is_set():
for line in file:
assert line == ""
stopping = threading.Event()
with tempfile.NamedTemporaryFile("w+") as f:
reader = threading.Thread(target=reader, args=(f, stopping))
reader.start()
barrier = threading.Barrier(N)
writers = [threading.Thread(target=writer, args=(f, barrier))
for _ in range(N)]
for t in writers:
t.start()
for t in writers:
t.join()
stopping.set()
reader.join()
f.flush()
assert(os.stat(f.name).st_size == COUNT * N)
...results in a crash:
python: ./Modules/_io/textio.c:1751: _io_TextIOWrapper_write_impl: Assertion `self->pending_bytes_count == 0' failed.
Aborted (core dumped)
The textiowrapper_iternext
method is not protected by a critical section and calls _textiowrapper_readline
, which calls _textiowrapper_writeflush
, which relies on the GIL or critical section to synchronise access to its internal data. In a free-threading build this means iterating over lines in a text file is not thread-safe and can crash if it races with writes or other operations.
It looks like all other entry points that could call _textiowrapper_writeflush
are protected by the critical section, so the easiest fix is probably to just likewise protect textiowrapper_iternext
.
CPython versions tested on:
CPython main branch
Operating systems tested on:
Linux
Output from running 'python -VV' on the command line:
No response