https://github.com/python/cpython/commit/d36e08099d56a54028174429a946c9816f284374 commit: d36e08099d56a54028174429a946c9816f284374 branch: 3.15 author: Miss Islington (bot) <[email protected]> committer: kumaraditya303 <[email protected]> date: 2026-05-19T10:12:25+05:30 summary:
[3.15] gh-149816: fix `dict.clear()` race on split-table dict with non-embedded values (GH-149914) (#150000) gh-149816: fix `dict.clear()` race on split-table dict with non-embedded values (GH-149914) (cherry picked from commit 169285470630b697c5e6e0e4c8091c31f25ffb04) Co-authored-by: Kumar Aditya <[email protected]> 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 55272a00c3ad50..dfe0634211d4b0 100644 --- a/Lib/test/test_free_threading/test_dict.py +++ b/Lib/test/test_free_threading/test_dict.py @@ -268,6 +268,34 @@ def watcher(): 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 diff --git a/Objects/dictobject.c b/Objects/dictobject.c index b33a273dac3b95..a7d67812bec925 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -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); } _______________________________________________ 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]
