[3.15] gh-153881: Atomically load `dk_nentries` in `_PyObject_IsInstanceDictEmpty` (GH-153882) (#154349)

kumaraditya303 <[email protected]>
Newsgroups gmane.comp.python.cvs
Message-ID <[email protected]>
https://github.com/python/cpython/commit/682610d700511439add24dcca62708b4c1644d3f
commit: 682610d700511439add24dcca62708b4c1644d3f
branch: 3.15
author: Miss Islington (bot) <[email protected]>
committer: kumaraditya303 <[email protected]>
date: 2026-07-21T18:17:37+05:30
summary:

[3.15] gh-153881: Atomically load `dk_nentries` in `_PyObject_IsInstanceDictEmpty` (GH-153882) (#154349)

gh-153881: Atomically load `dk_nentries` in `_PyObject_IsInstanceDictEmpty` (GH-153882)
(cherry picked from commit 7ebe773160327026ba5393b27e2e2860db4d08b2)

Co-authored-by: Brij Kapadia <[email protected]>

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 eebb896f5d785b2..29b6361eea4feb4 100644
--- a/Objects/dictobject.c
+++ b/Objects/dictobject.c
@@ -7654,7 +7654,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]
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.