]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-153881: Atomically load `dk_nentries` in `_PyObject_IsInstanceDictEmpty` (#153882)
authorBrij Kapadia <97006829+brijkapadia@users.noreply.github.com>
Tue, 21 Jul 2026 12:07:17 +0000 (08:07 -0400)
committerGitHub <noreply@github.com>
Tue, 21 Jul 2026 12:07:17 +0000 (17:37 +0530)
Lib/test/test_free_threading/test_dict.py
Misc/NEWS.d/next/Core_and_Builtins/2026-07-17-22-03-39.gh-issue-153881.oDa06s.rst [new file with mode: 0644]
Objects/dictobject.c

index ad23290a92ab3458f760741540e7daf102f7285c..4a812275143bc4d72361da628a659538564b292e 100644 (file)
@@ -336,5 +336,25 @@ class TestDict(TestCase):
         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 (file)
index 0000000..8facf88
--- /dev/null
@@ -0,0 +1,2 @@
+Fix potential data race when calling :meth:`~object.__getstate__`
+under the :term:`free-threaded build`.
index 246ffe6c18e9b51cccef845ab242afaf9ff0d23d..c650aa456d2cc9d40d273becdd0233ca0e74b71c 100644 (file)
@@ -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;
                 }