]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-149816: fix `dict.clear()` race on split-table dict with non-embedded values ...
authorKumar Aditya <kumaraditya@python.org>
Mon, 18 May 2026 16:09:45 +0000 (21:39 +0530)
committerGitHub <noreply@github.com>
Mon, 18 May 2026 16:09:45 +0000 (21:39 +0530)
Lib/test/test_free_threading/test_dict.py
Objects/dictobject.c

index 55272a00c3ad501707c30484286263913dd10e0d..dfe0634211d4b02fedcaf09ab4a9982dcd9b1f6d 100644 (file)
@@ -268,6 +268,34 @@ class TestDict(TestCase):
         finally:
             _testcapi.clear_dict_watcher(wid)
 
+    def test_racing_split_dict_clear_and_lookup(self):
+        class C:
+            pass
+
+        keys = [f"a{i}" for i in range(16)]
+
+        def make_split_nonembedded():
+            inst = C()
+            for key in keys:
+                setattr(inst, key, keys.index(key))
+            # dict.copy() of a split instance dict yields a split table
+            # with non-embedded values
+            return inst.__dict__.copy()
+
+        d = make_split_nonembedded()
+
+        def clearer():
+            for _ in range(1000):
+                d.clear()
+                d.update(make_split_nonembedded())
+
+        def reader():
+            for _ in range(1000):
+                for k in keys:
+                    d.get(k)
+
+        threading_helper.run_concurrently([clearer, reader, reader])
+
     def test_racing_dict_update_and_method_lookup(self):
         # gh-144295: test race between dict modifications and method lookups.
         # Uses BytesIO because the race requires a type without Py_TPFLAGS_INLINE_VALUES
index b33a273dac3b95b904ad03aa2004b53321be673d..a7d67812bec9250acc9bdd4d6abbea968f255cf8 100644 (file)
@@ -3083,10 +3083,12 @@ clear_lock_held(PyObject *op)
         set_keys(mp, Py_EMPTY_KEYS);
         n = oldkeys->dk_nentries;
         for (i = 0; i < n; i++) {
-            Py_CLEAR(oldvalues->values[i]);
+            PyObject *tmp = oldvalues->values[i];
+            FT_ATOMIC_STORE_PTR_RELEASE(oldvalues->values[i], NULL);
+            Py_XDECREF(tmp);
         }
         free_values(oldvalues, IS_DICT_SHARED(mp));
-        dictkeys_decref(oldkeys, false);
+        dictkeys_decref(oldkeys, IS_DICT_SHARED(mp));
     }
     ASSERT_CONSISTENT(mp);
 }