https://github.com/python/cpython/commit/06f9c8ca1cb9abe92507108a299a65ee868d07e3
commit: 06f9c8ca1cb9abe92507108a299a65ee868d07e3
branch: 3.14
author: Sam Gross <[email protected]>
committer: colesbury <[email protected]>
date: 2026-01-30T12:15:47-05:00
summary:

[3.14] gh-144295: Fix data race in dict method lookup and global load 
(gh-144312) (#144346)

In `_Py_dict_lookup_threadsafe_stackref`, call `ensure_shared_on_read()` to
prevent a race between the lookup and concurrent dict resizes, which may free
the PyDictKeysObject (i.e., it ensures that the resize uses QSBR).

(cherry picked from commit e666a01ef42939f77f4c22ca47a610df5ef8b7ab)

files:
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 5d5d4e226caa40..1ffd924e9f477a 100644
--- a/Lib/test/test_free_threading/test_dict.py
+++ b/Lib/test/test_free_threading/test_dict.py
@@ -245,5 +245,27 @@ def reader():
         with threading_helper.start_threads([t1, t2]):
             pass
 
+    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
+        # for the _PyDict_GetMethodStackRef code path.
+        import io
+        obj = io.BytesIO()
+
+        def writer():
+            for _ in range(10000):
+                obj.x = 1
+                del obj.x
+
+        def reader():
+            for _ in range(10000):
+                obj.getvalue()
+
+        t1 = Thread(target=writer)
+        t2 = Thread(target=reader)
+
+        with threading_helper.start_threads([t1, t2]):
+            pass
+
 if __name__ == "__main__":
     unittest.main()
diff --git a/Objects/dictobject.c b/Objects/dictobject.c
index e7d5088ded7b4c..c2223af950064c 100644
--- a/Objects/dictobject.c
+++ b/Objects/dictobject.c
@@ -1587,7 +1587,9 @@ _Py_dict_lookup_threadsafe(PyDictObject *mp, PyObject 
*key, Py_hash_t hash, PyOb
 Py_ssize_t
 _Py_dict_lookup_threadsafe_stackref(PyDictObject *mp, PyObject *key, Py_hash_t 
hash, _PyStackRef *value_addr)
 {
-    PyDictKeysObject *dk = _Py_atomic_load_ptr(&mp->ma_keys);
+    ensure_shared_on_read(mp);
+
+    PyDictKeysObject *dk = _Py_atomic_load_ptr_acquire(&mp->ma_keys);
     if (dk->dk_kind == DICT_KEYS_UNICODE && PyUnicode_CheckExact(key)) {
         Py_ssize_t ix = unicodekeys_lookup_unicode_threadsafe(dk, key, hash);
         if (ix == DKIX_EMPTY) {

_______________________________________________
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]

Reply via email to