Skip to content

Commit 06f9c8c

Browse files
authored
[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 e666a01)
1 parent 49ce23f commit 06f9c8c

File tree

2 files changed

+25
-1
lines changed

2 files changed

+25
-1
lines changed

Lib/test/test_free_threading/test_dict.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,5 +245,27 @@ def reader():
245245
with threading_helper.start_threads([t1, t2]):
246246
pass
247247

248+
def test_racing_dict_update_and_method_lookup(self):
249+
# gh-144295: test race between dict modifications and method lookups.
250+
# Uses BytesIO because the race requires a type without Py_TPFLAGS_INLINE_VALUES
251+
# for the _PyDict_GetMethodStackRef code path.
252+
import io
253+
obj = io.BytesIO()
254+
255+
def writer():
256+
for _ in range(10000):
257+
obj.x = 1
258+
del obj.x
259+
260+
def reader():
261+
for _ in range(10000):
262+
obj.getvalue()
263+
264+
t1 = Thread(target=writer)
265+
t2 = Thread(target=reader)
266+
267+
with threading_helper.start_threads([t1, t2]):
268+
pass
269+
248270
if __name__ == "__main__":
249271
unittest.main()

Objects/dictobject.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1587,7 +1587,9 @@ _Py_dict_lookup_threadsafe(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyOb
15871587
Py_ssize_t
15881588
_Py_dict_lookup_threadsafe_stackref(PyDictObject *mp, PyObject *key, Py_hash_t hash, _PyStackRef *value_addr)
15891589
{
1590-
PyDictKeysObject *dk = _Py_atomic_load_ptr(&mp->ma_keys);
1590+
ensure_shared_on_read(mp);
1591+
1592+
PyDictKeysObject *dk = _Py_atomic_load_ptr_acquire(&mp->ma_keys);
15911593
if (dk->dk_kind == DICT_KEYS_UNICODE && PyUnicode_CheckExact(key)) {
15921594
Py_ssize_t ix = unicodekeys_lookup_unicode_threadsafe(dk, key, hash);
15931595
if (ix == DKIX_EMPTY) {

0 commit comments

Comments
 (0)