From: Peter Bierma Date: Thu, 12 Jun 2025 11:11:05 +0000 (-0400) Subject: gh-135410: use a critical section around `StringIO.__next__` (#135412) X-Git-Tag: v3.15.0a1~1323 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=e6c3039cb39e68ae9af9ddcaca341c5af8f9cf23;p=thirdparty%2FPython%2Fcpython.git gh-135410: use a critical section around `StringIO.__next__` (#135412) --- diff --git a/Lib/test/test_memoryio.py b/Lib/test/test_memoryio.py index 63998a86c45b..249e0f3ba32f 100644 --- a/Lib/test/test_memoryio.py +++ b/Lib/test/test_memoryio.py @@ -5,6 +5,7 @@ BytesIO -- for bytes import unittest from test import support +from test.support import threading_helper import gc import io @@ -12,6 +13,7 @@ import _pyio as pyio import pickle import sys import weakref +import threading class IntLike: def __init__(self, num): @@ -723,6 +725,22 @@ class TextIOTestMixin: for newline in (None, "", "\n", "\r", "\r\n"): self.ioclass(newline=newline) + @unittest.skipUnless(support.Py_GIL_DISABLED, "only meaningful under free-threading") + @threading_helper.requires_working_threading() + def test_concurrent_use(self): + memio = self.ioclass("") + + def use(): + memio.write("x" * 10) + memio.readlines() + + threads = [threading.Thread(target=use) for _ in range(8)] + with threading_helper.catch_threading_exception() as cm: + with threading_helper.start_threads(threads): + pass + + self.assertIsNone(cm.exc_value) + class PyStringIOTest(MemoryTestMixin, MemorySeekTestMixin, TextIOTestMixin, unittest.TestCase): @@ -890,6 +908,7 @@ class CStringIOTest(PyStringIOTest): self.assertRaises(ValueError, memio.__setstate__, ("closed", "", 0, None)) + class CStringIOPickleTest(PyStringIOPickleTest): UnsupportedOperation = io.UnsupportedOperation diff --git a/Misc/NEWS.d/next/Library/2025-06-11-19-05-49.gh-issue-135410.E89Boi.rst b/Misc/NEWS.d/next/Library/2025-06-11-19-05-49.gh-issue-135410.E89Boi.rst new file mode 100644 index 000000000000..a5917fba3f7b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-06-11-19-05-49.gh-issue-135410.E89Boi.rst @@ -0,0 +1,2 @@ +Fix a crash when iterating over :class:`io.StringIO` on the :term:`free +threaded ` build. diff --git a/Modules/_io/stringio.c b/Modules/_io/stringio.c index 56913fafefba..8482c268176c 100644 --- a/Modules/_io/stringio.c +++ b/Modules/_io/stringio.c @@ -404,7 +404,7 @@ _io_StringIO_readline_impl(stringio *self, Py_ssize_t size) } static PyObject * -stringio_iternext(PyObject *op) +stringio_iternext_lock_held(PyObject *op) { PyObject *line; stringio *self = stringio_CAST(op); @@ -441,6 +441,16 @@ stringio_iternext(PyObject *op) return line; } +static PyObject * +stringio_iternext(PyObject *op) +{ + PyObject *res; + Py_BEGIN_CRITICAL_SECTION(op); + res = stringio_iternext_lock_held(op); + Py_END_CRITICAL_SECTION(); + return res; +} + /*[clinic input] @critical_section _io.StringIO.truncate