From fee7d79d1de4cb67f46d4e07d1796a4470eba3cc Mon Sep 17 00:00:00 2001 From: Dino Viehland Date: Wed, 24 Jul 2024 16:35:14 -0700 Subject: [PATCH] Deliver dict added event only after it's guaranteed to succeed Summary: Back port of: https://github.com/python/cpython/pull/122207 fixing https://github.com/python/cpython/issues/122208 The current dictionary watchers implementation delivers the added event before it checks to see if we need to re-size the dictionary. This resize can fail so the value isn't added, and then the tracker is out of sync with the true state of the dictionary. This moves the delivery of the event to after any necessary allocations have happened. Reviewed By: jbower-fb Differential Revision: D60182094 fbshipit-source-id: f34940e98ce1caadeee364f9d126d35839661961 --- Objects/dictobject.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 52f97c11b54..2cde508c9ac 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -1349,8 +1349,6 @@ insertdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value) } if (ix == DKIX_EMPTY) { - uint64_t new_version = _PyDict_NotifyEvent( - PyDict_EVENT_ADDED, mp, key, value); /* Insert into new slot. */ assert(old_value == NULL); if (mp->ma_keys->dk_usable <= 0) { @@ -1358,6 +1356,8 @@ insertdict(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject *value) if (insertion_resize(mp) < 0) goto Fail; } + uint64_t new_version = _PyDict_NotifyEvent( + PyDict_EVENT_ADDED, mp, key, value); if (!PyUnicode_CheckExact(key)) { if (mp->ma_keys->dk_lookup == lookdict_with_lazy_imports_unicode) { mp->ma_keys->dk_lookup = lookdict_with_lazy_imports; @@ -1428,13 +1428,13 @@ insert_to_emptydict(PyDictObject *mp, PyObject *key, Py_hash_t hash, { assert(mp->ma_keys == Py_EMPTY_KEYS); - uint64_t new_version = _PyDict_NotifyEvent( - PyDict_EVENT_ADDED, mp, key, value); - PyDictKeysObject *newkeys = new_keys_object(PyDict_MINSIZE); if (newkeys == NULL) { return -1; } + uint64_t new_version = _PyDict_NotifyEvent( + PyDict_EVENT_ADDED, mp, key, value); + dictkeys_decref(Py_EMPTY_KEYS); mp->ma_keys = newkeys; mp->ma_values = NULL; @@ -3517,14 +3517,14 @@ PyDict_SetDefault(PyObject *d, PyObject *key, PyObject *defaultobj) if (ix == DKIX_EMPTY) { PyDictKeyEntry *ep, *ep0; - uint64_t new_version = _PyDict_NotifyEvent( - PyDict_EVENT_ADDED, mp, key, defaultobj); value = defaultobj; if (mp->ma_keys->dk_usable <= 0) { if (insertion_resize(mp) < 0) { return NULL; } } + uint64_t new_version = _PyDict_NotifyEvent( + PyDict_EVENT_ADDED, mp, key, defaultobj); if (!PyUnicode_CheckExact(key) && mp->ma_keys->dk_lookup != lookdict) { mp->ma_keys->dk_lookup = lookdict; }