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)
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()
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) {