From c8f5ca6810aeb13f9f43d17d3f0befd550a77dba Mon Sep 17 00:00:00 2001 From: Dino Viehland Date: Tue, 30 Jul 2024 09:13:40 -0700 Subject: [PATCH] [3.12] gh-122208: Don't delivery PyDict_EVENT_ADDED until it can't fail (#122327) Don't delivery PyDict_EVENT_ADDED until it can't fail --- ...-07-26-21-38-47.gh-issue-122208.z8KHsY.rst | 1 + Objects/dictobject.c | 21 ++++++++++--------- 2 files changed, 12 insertions(+), 10 deletions(-) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2024-07-26-21-38-47.gh-issue-122208.z8KHsY.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-07-26-21-38-47.gh-issue-122208.z8KHsY.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-07-26-21-38-47.gh-issue-122208.z8KHsY.rst new file mode 100644 index 00000000000..e4a89d137ed --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-07-26-21-38-47.gh-issue-122208.z8KHsY.rst @@ -0,0 +1 @@ +Dictionary watchers now only deliver the PyDict_EVENT_ADDED event when the insertion is in a known good state to succeed. diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 254cd9ad2f9..a99f32a9c84 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -1251,10 +1251,6 @@ insertdict(PyInterpreterState *interp, PyDictObject *mp, MAINTAIN_TRACKING(mp, key, value); if (ix == DKIX_EMPTY) { - uint64_t new_version = _PyDict_NotifyEvent( - interp, PyDict_EVENT_ADDED, mp, key, value); - /* Insert into new slot. */ - mp->ma_keys->dk_version = 0; assert(old_value == NULL); if (mp->ma_keys->dk_usable <= 0) { /* Need to resize. */ @@ -1262,6 +1258,11 @@ insertdict(PyInterpreterState *interp, PyDictObject *mp, goto Fail; } + uint64_t new_version = _PyDict_NotifyEvent( + interp, PyDict_EVENT_ADDED, mp, key, value); + /* Insert into new slot. */ + mp->ma_keys->dk_version = 0; + Py_ssize_t hashpos = find_empty_slot(mp->ma_keys, hash); dictkeys_set_index(mp->ma_keys, hashpos, mp->ma_keys->dk_nentries); @@ -1335,9 +1336,6 @@ insert_to_emptydict(PyInterpreterState *interp, PyDictObject *mp, { assert(mp->ma_keys == Py_EMPTY_KEYS); - uint64_t new_version = _PyDict_NotifyEvent( - interp, PyDict_EVENT_ADDED, mp, key, value); - int unicode = PyUnicode_CheckExact(key); PyDictKeysObject *newkeys = new_keys_object( interp, PyDict_LOG_MINSIZE, unicode); @@ -1346,6 +1344,9 @@ insert_to_emptydict(PyInterpreterState *interp, PyDictObject *mp, Py_DECREF(value); return -1; } + uint64_t new_version = _PyDict_NotifyEvent( + interp, PyDict_EVENT_ADDED, mp, key, value); + /* We don't decref Py_EMPTY_KEYS here because it is immortal. */ mp->ma_keys = newkeys; mp->ma_values = NULL; @@ -3324,15 +3325,15 @@ PyDict_SetDefault(PyObject *d, PyObject *key, PyObject *defaultobj) return NULL; if (ix == DKIX_EMPTY) { - uint64_t new_version = _PyDict_NotifyEvent( - interp, PyDict_EVENT_ADDED, mp, key, defaultobj); - mp->ma_keys->dk_version = 0; value = defaultobj; if (mp->ma_keys->dk_usable <= 0) { if (insertion_resize(interp, mp, 1) < 0) { return NULL; } } + uint64_t new_version = _PyDict_NotifyEvent( + interp, PyDict_EVENT_ADDED, mp, key, defaultobj); + mp->ma_keys->dk_version = 0; Py_ssize_t hashpos = find_empty_slot(mp->ma_keys, hash); dictkeys_set_index(mp->ma_keys, hashpos, mp->ma_keys->dk_nentries); if (DK_IS_UNICODE(mp->ma_keys)) {