+import codecs
import io
import _pyio as pyio
import threading
from unittest import TestCase
from test.support import threading_helper
+from test.support.threading_helper import run_concurrently
from random import randint
from sys import getsizeof
+threading_helper.requires_working_threading(module=True)
+
class ThreadSafetyMixin:
# Test pretty much everything that can break under free-threading.
class PyBytesIOTest(ThreadSafetyMixin, TestCase):
ioclass = pyio.BytesIO
+
+
+class IncrementalNewlineDecoderTest(TestCase):
+ def make_decoder(self):
+ utf8_decoder = codecs.getincrementaldecoder('utf-8')()
+ return io.IncrementalNewlineDecoder(utf8_decoder, translate=True)
+
+ def test_concurrent_reset(self):
+ decoder = self.make_decoder()
+
+ def worker():
+ for _ in range(100):
+ decoder.reset()
+
+ run_concurrently(worker_func=worker, nthreads=2)
+
+ def test_concurrent_decode(self):
+ decoder = self.make_decoder()
+
+ def worker():
+ for _ in range(100):
+ decoder.decode(b"line\r\n", final=False)
+
+ run_concurrently(worker_func=worker, nthreads=2)
+
+ def test_concurrent_getstate_setstate(self):
+ decoder = self.make_decoder()
+ state = decoder.getstate()
+
+ def getstate_worker():
+ for _ in range(100):
+ decoder.getstate()
+
+ def setstate_worker():
+ for _ in range(100):
+ decoder.setstate(state)
+
+ run_concurrently([getstate_worker] * 2 + [setstate_worker] * 2)
+
+ def test_concurrent_decode_and_reset(self):
+ decoder = self.make_decoder()
+
+ def decode_worker():
+ for _ in range(100):
+ decoder.decode(b"line\r\n", final=False)
+
+ def reset_worker():
+ for _ in range(100):
+ decoder.reset()
+
+ run_concurrently([decode_worker] * 2 + [reset_worker] * 2)
--- /dev/null
+Fix data races in :class:`io.IncrementalNewlineDecoder` in the :term:`free-threaded build`.
goto exit;
}
skip_optional_pos:
+ Py_BEGIN_CRITICAL_SECTION(self);
return_value = _io_IncrementalNewlineDecoder_decode_impl((nldecoder_object *)self, input, final);
+ Py_END_CRITICAL_SECTION();
exit:
return return_value;
static PyObject *
_io_IncrementalNewlineDecoder_getstate(PyObject *self, PyObject *Py_UNUSED(ignored))
{
- return _io_IncrementalNewlineDecoder_getstate_impl((nldecoder_object *)self);
+ PyObject *return_value = NULL;
+
+ Py_BEGIN_CRITICAL_SECTION(self);
+ return_value = _io_IncrementalNewlineDecoder_getstate_impl((nldecoder_object *)self);
+ Py_END_CRITICAL_SECTION();
+
+ return return_value;
}
PyDoc_STRVAR(_io_IncrementalNewlineDecoder_setstate__doc__,
{
PyObject *return_value = NULL;
+ Py_BEGIN_CRITICAL_SECTION(self);
return_value = _io_IncrementalNewlineDecoder_setstate_impl((nldecoder_object *)self, state);
+ Py_END_CRITICAL_SECTION();
return return_value;
}
static PyObject *
_io_IncrementalNewlineDecoder_reset(PyObject *self, PyObject *Py_UNUSED(ignored))
{
- return _io_IncrementalNewlineDecoder_reset_impl((nldecoder_object *)self);
+ PyObject *return_value = NULL;
+
+ Py_BEGIN_CRITICAL_SECTION(self);
+ return_value = _io_IncrementalNewlineDecoder_reset_impl((nldecoder_object *)self);
+ Py_END_CRITICAL_SECTION();
+
+ return return_value;
}
PyDoc_STRVAR(_io_TextIOWrapper___init____doc__,
return return_value;
}
-/*[clinic end generated code: output=30404271a1151056 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=c38e6cd5ff4b7eea input=a9049054013a1b77]*/
}
/*[clinic input]
+@critical_section
_io.IncrementalNewlineDecoder.decode
input: object
final: bool = False
static PyObject *
_io_IncrementalNewlineDecoder_decode_impl(nldecoder_object *self,
PyObject *input, int final)
-/*[clinic end generated code: output=0d486755bb37a66e input=90e223c70322c5cd]*/
+/*[clinic end generated code: output=0d486755bb37a66e input=9475d16a73168504]*/
{
return _PyIncrementalNewlineDecoder_decode((PyObject *) self, input, final);
}
/*[clinic input]
+@critical_section
_io.IncrementalNewlineDecoder.getstate
[clinic start generated code]*/
static PyObject *
_io_IncrementalNewlineDecoder_getstate_impl(nldecoder_object *self)
-/*[clinic end generated code: output=f0d2c9c136f4e0d0 input=f8ff101825e32e7f]*/
+/*[clinic end generated code: output=f0d2c9c136f4e0d0 input=dc3e1f27aa850f12]*/
{
PyObject *buffer;
unsigned long long flag;
}
/*[clinic input]
+@critical_section
_io.IncrementalNewlineDecoder.setstate
state: object
/
static PyObject *
_io_IncrementalNewlineDecoder_setstate_impl(nldecoder_object *self,
PyObject *state)
-/*[clinic end generated code: output=09135cb6e78a1dc8 input=c53fb505a76dbbe2]*/
+/*[clinic end generated code: output=09135cb6e78a1dc8 input=275fd3982d2b08cb]*/
{
PyObject *buffer;
unsigned long long flag;
}
/*[clinic input]
+@critical_section
_io.IncrementalNewlineDecoder.reset
[clinic start generated code]*/
static PyObject *
_io_IncrementalNewlineDecoder_reset_impl(nldecoder_object *self)
-/*[clinic end generated code: output=32fa40c7462aa8ff input=728678ddaea776df]*/
+/*[clinic end generated code: output=32fa40c7462aa8ff input=31bd8ae4e36cec83]*/
{
CHECK_INITIALIZED_DECODER(self);