mirror of
https://github.com/python/cpython.git
synced 2025-10-01 04:42:10 +00:00
[3.14] gh-134908: Protect textiowrapper_iternext
with critical section (gh-134910) (gh-135039)
The `textiowrapper_iternext` function called `_textiowrapper_writeflush`, but did not
use a critical section, making it racy in free-threaded builds.
(cherry picked from commit 44fb7c361c
)
Co-authored-by: Duane Griffin <duaneg@dghda.com>
This commit is contained in:
parent
7ac461883d
commit
428b0ca114
3 changed files with 46 additions and 1 deletions
|
@ -1062,6 +1062,37 @@ class IOTest(unittest.TestCase):
|
||||||
# Silence destructor error
|
# Silence destructor error
|
||||||
R.flush = lambda self: None
|
R.flush = lambda self: None
|
||||||
|
|
||||||
|
@threading_helper.requires_working_threading()
|
||||||
|
def test_write_readline_races(self):
|
||||||
|
# gh-134908: Concurrent iteration over a file caused races
|
||||||
|
thread_count = 2
|
||||||
|
write_count = 100
|
||||||
|
read_count = 100
|
||||||
|
|
||||||
|
def writer(file, barrier):
|
||||||
|
barrier.wait()
|
||||||
|
for _ in range(write_count):
|
||||||
|
file.write("x")
|
||||||
|
|
||||||
|
def reader(file, barrier):
|
||||||
|
barrier.wait()
|
||||||
|
for _ in range(read_count):
|
||||||
|
for line in file:
|
||||||
|
self.assertEqual(line, "")
|
||||||
|
|
||||||
|
with self.open(os_helper.TESTFN, "w+") as f:
|
||||||
|
barrier = threading.Barrier(thread_count + 1)
|
||||||
|
reader = threading.Thread(target=reader, args=(f, barrier))
|
||||||
|
writers = [threading.Thread(target=writer, args=(f, barrier))
|
||||||
|
for _ in range(thread_count)]
|
||||||
|
with threading_helper.catch_threading_exception() as cm:
|
||||||
|
with threading_helper.start_threads(writers + [reader]):
|
||||||
|
pass
|
||||||
|
self.assertIsNone(cm.exc_type)
|
||||||
|
|
||||||
|
self.assertEqual(os.stat(os_helper.TESTFN).st_size,
|
||||||
|
write_count * thread_count)
|
||||||
|
|
||||||
|
|
||||||
class CIOTest(IOTest):
|
class CIOTest(IOTest):
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Fix crash when iterating over lines in a text file on the :term:`free threaded <free threading>` build.
|
|
@ -1578,6 +1578,8 @@ _io_TextIOWrapper_detach_impl(textio *self)
|
||||||
static int
|
static int
|
||||||
_textiowrapper_writeflush(textio *self)
|
_textiowrapper_writeflush(textio *self)
|
||||||
{
|
{
|
||||||
|
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(self);
|
||||||
|
|
||||||
if (self->pending_bytes == NULL)
|
if (self->pending_bytes == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -3173,8 +3175,9 @@ _io_TextIOWrapper_close_impl(textio *self)
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
textiowrapper_iternext(PyObject *op)
|
textiowrapper_iternext_lock_held(PyObject *op)
|
||||||
{
|
{
|
||||||
|
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(op);
|
||||||
PyObject *line;
|
PyObject *line;
|
||||||
textio *self = textio_CAST(op);
|
textio *self = textio_CAST(op);
|
||||||
|
|
||||||
|
@ -3210,6 +3213,16 @@ textiowrapper_iternext(PyObject *op)
|
||||||
return line;
|
return line;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
textiowrapper_iternext(PyObject *op)
|
||||||
|
{
|
||||||
|
PyObject *result;
|
||||||
|
Py_BEGIN_CRITICAL_SECTION(op);
|
||||||
|
result = textiowrapper_iternext_lock_held(op);
|
||||||
|
Py_END_CRITICAL_SECTION();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/*[clinic input]
|
/*[clinic input]
|
||||||
@critical_section
|
@critical_section
|
||||||
@getter
|
@getter
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue