Skip to content

Commit e6c3039

Browse files
gh-135410: use a critical section around StringIO.__next__ (#135412)
1 parent d447129 commit e6c3039

File tree

3 files changed

+32
-1
lines changed

3 files changed

+32
-1
lines changed

Lib/test/test_memoryio.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@
55

66
import unittest
77
from test import support
8+
from test.support import threading_helper
89

910
import gc
1011
import io
1112
import _pyio as pyio
1213
import pickle
1314
import sys
1415
import weakref
16+
import threading
1517

1618
class IntLike:
1719
def __init__(self, num):
@@ -723,6 +725,22 @@ def test_newline_argument(self):
723725
for newline in (None, "", "\n", "\r", "\r\n"):
724726
self.ioclass(newline=newline)
725727

728+
@unittest.skipUnless(support.Py_GIL_DISABLED, "only meaningful under free-threading")
729+
@threading_helper.requires_working_threading()
730+
def test_concurrent_use(self):
731+
memio = self.ioclass("")
732+
733+
def use():
734+
memio.write("x" * 10)
735+
memio.readlines()
736+
737+
threads = [threading.Thread(target=use) for _ in range(8)]
738+
with threading_helper.catch_threading_exception() as cm:
739+
with threading_helper.start_threads(threads):
740+
pass
741+
742+
self.assertIsNone(cm.exc_value)
743+
726744

727745
class PyStringIOTest(MemoryTestMixin, MemorySeekTestMixin,
728746
TextIOTestMixin, unittest.TestCase):
@@ -890,6 +908,7 @@ def test_setstate(self):
890908
self.assertRaises(ValueError, memio.__setstate__, ("closed", "", 0, None))
891909

892910

911+
893912
class CStringIOPickleTest(PyStringIOPickleTest):
894913
UnsupportedOperation = io.UnsupportedOperation
895914

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix a crash when iterating over :class:`io.StringIO` on the :term:`free
2+
threaded <free threading>` build.

Modules/_io/stringio.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ _io_StringIO_readline_impl(stringio *self, Py_ssize_t size)
404404
}
405405

406406
static PyObject *
407-
stringio_iternext(PyObject *op)
407+
stringio_iternext_lock_held(PyObject *op)
408408
{
409409
PyObject *line;
410410
stringio *self = stringio_CAST(op);
@@ -441,6 +441,16 @@ stringio_iternext(PyObject *op)
441441
return line;
442442
}
443443

444+
static PyObject *
445+
stringio_iternext(PyObject *op)
446+
{
447+
PyObject *res;
448+
Py_BEGIN_CRITICAL_SECTION(op);
449+
res = stringio_iternext_lock_held(op);
450+
Py_END_CRITICAL_SECTION();
451+
return res;
452+
}
453+
444454
/*[clinic input]
445455
@critical_section
446456
_io.StringIO.truncate

0 commit comments

Comments
 (0)