Skip to content

Commit 8cb7d9a

Browse files
[3.14] gh-135410: use a critical section around StringIO.__next__ (GH-135412) (#135425)
gh-135410: use a critical section around `StringIO.__next__` (GH-135412) (cherry picked from commit e6c3039) Co-authored-by: Peter Bierma <zintensitydev@gmail.com>
1 parent e76dbc8 commit 8cb7d9a

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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy