https://github.com/python/cpython/commit/6c026ad6c49cb7e9fa4a0867f2d0b4071a37a483
commit: 6c026ad6c49cb7e9fa4a0867f2d0b4071a37a483
branch: main
author: Bhuvi <[email protected]>
committer: kumaraditya303 <[email protected]>
date: 2026-07-25T11:26:33+05:30
summary:
gh-153290: Fix data race in BytesIO.__setstate__ installing __dict__ (#153376)
files:
A Misc/NEWS.d/next/Library/2026-07-09-08-40-00.gh-issue-153290.GTzSEa.rst
M Lib/test/test_free_threading/test_io.py
M Modules/_io/bytesio.c
diff --git a/Lib/test/test_free_threading/test_io.py
b/Lib/test/test_free_threading/test_io.py
index e0bd7e211e73024..057e0adf3b42bc4 100644
--- a/Lib/test/test_free_threading/test_io.py
+++ b/Lib/test/test_free_threading/test_io.py
@@ -122,6 +122,34 @@ def sizeof(barrier, b, *ignore):
class CBytesIOTest(ThreadSafetyMixin, TestCase):
ioclass = io.BytesIO
+ @threading_helper.requires_working_threading()
+ @threading_helper.reap_threads
+ def test_concurrent_setstate_and_method_call(self):
+ # gh-153290: __setstate__() installed the instance __dict__ with a
+ # plain store, racing the lock-free LOAD_ATTR method fast path that
+ # reads the dict slot with an atomic acquire load.
+ states = [(b"A" * 64, 0, {}), (b"B" * 128, 32, {}), (b"C" * 256, 0,
{})]
+ nreaders = 4
+ for _ in range(25):
+ shared = self.ioclass(b"initial payload")
+ barrier = threading.Barrier(1 + nreaders)
+
+ def setter():
+ barrier.wait()
+ for state in states:
+ shared.__setstate__(state)
+
+ def reader():
+ barrier.wait()
+ for _ in range(100):
+ shared.read(8)
+
+ threads = [threading.Thread(target=setter)]
+ threads += [threading.Thread(target=reader)
+ for _ in range(nreaders)]
+ with threading_helper.start_threads(threads):
+ pass
+
@threading_helper.requires_working_threading()
@threading_helper.reap_threads
def test_concurrent_whole_buffer_read_and_resize(self):
diff --git
a/Misc/NEWS.d/next/Library/2026-07-09-08-40-00.gh-issue-153290.GTzSEa.rst
b/Misc/NEWS.d/next/Library/2026-07-09-08-40-00.gh-issue-153290.GTzSEa.rst
new file mode 100644
index 000000000000000..deea3fe66b9b5af
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-07-09-08-40-00.gh-issue-153290.GTzSEa.rst
@@ -0,0 +1,3 @@
+Fix a data race on the free-threaded build when
:meth:`!io.BytesIO.__setstate__`
+installs the instance dictionary while another thread concurrently calls a
+method on the same object.
diff --git a/Modules/_io/bytesio.c b/Modules/_io/bytesio.c
index 3d14ec3f8f94a92..7d6053d85cd9e4a 100644
--- a/Modules/_io/bytesio.c
+++ b/Modules/_io/bytesio.c
@@ -1054,7 +1054,9 @@ bytesio_setstate_lock_held(PyObject *op, PyObject *state)
return NULL;
}
else {
- self->dict = Py_NewRef(dict);
+ /* The LOAD_ATTR specializations read the dict slot lock-free
+ with an acquire load, so pair it with a release store. */
+ FT_ATOMIC_STORE_PTR_RELEASE(self->dict, Py_NewRef(dict));
}
}
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]