https://github.com/python/cpython/commit/7ebe773160327026ba5393b27e2e2860db4d08b2
commit: 7ebe773160327026ba5393b27e2e2860db4d08b2
branch: main
author: Brij Kapadia <[email protected]>
committer: kumaraditya303 <[email protected]>
date: 2026-07-21T17:37:17+05:30
summary:
gh-153881: Atomically load `dk_nentries` in `_PyObject_IsInstanceDictEmpty`
(#153882)
files:
A
Misc/NEWS.d/next/Core_and_Builtins/2026-07-17-22-03-39.gh-issue-153881.oDa06s.rst
M Lib/test/test_free_threading/test_dict.py
M Objects/dictobject.c
diff --git a/Lib/test/test_free_threading/test_dict.py
b/Lib/test/test_free_threading/test_dict.py
index ad23290a92ab345..4a812275143bc4d 100644
--- a/Lib/test/test_free_threading/test_dict.py
+++ b/Lib/test/test_free_threading/test_dict.py
@@ -336,5 +336,25 @@ def reader():
with threading_helper.start_threads([t1, t2]):
pass
+ def test_getstate_race_with_shared_keys(self):
+ box = [None]
+ enter = Barrier(2)
+ leave = Barrier(2)
+
+ def reader():
+ for _ in range(1000):
+ enter.wait()
+ box[0].__getstate__()
+ leave.wait()
+
+ def writer():
+ for i in range(1000):
+ box[0] = type(f"C{i}", (), {})()
+ enter.wait()
+ setattr(box[0], str(i), 1)
+ leave.wait()
+
+ threading_helper.run_concurrently([reader, writer])
+
if __name__ == "__main__":
unittest.main()
diff --git
a/Misc/NEWS.d/next/Core_and_Builtins/2026-07-17-22-03-39.gh-issue-153881.oDa06s.rst
b/Misc/NEWS.d/next/Core_and_Builtins/2026-07-17-22-03-39.gh-issue-153881.oDa06s.rst
new file mode 100644
index 000000000000000..8facf88367fda7f
--- /dev/null
+++
b/Misc/NEWS.d/next/Core_and_Builtins/2026-07-17-22-03-39.gh-issue-153881.oDa06s.rst
@@ -0,0 +1,2 @@
+Fix potential data race when calling :meth:`~object.__getstate__`
+under the :term:`free-threaded build`.
diff --git a/Objects/dictobject.c b/Objects/dictobject.c
index 246ffe6c18e9b51..c650aa456d2cc9d 100644
--- a/Objects/dictobject.c
+++ b/Objects/dictobject.c
@@ -7722,7 +7722,7 @@ _PyObject_IsInstanceDictEmpty(PyObject *obj)
PyDictValues *values = _PyObject_InlineValues(obj);
if (FT_ATOMIC_LOAD_UINT8(values->valid)) {
PyDictKeysObject *keys = CACHED_KEYS(tp);
- for (Py_ssize_t i = 0; i < keys->dk_nentries; i++) {
+ for (Py_ssize_t i = 0; i < LOAD_KEYS_NENTRIES(keys); i++) {
if (FT_ATOMIC_LOAD_PTR_RELAXED(values->values[i]) != NULL) {
return 0;
}
_______________________________________________
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]