Skip to content

Commit

Permalink
Deliver dict added event only after it's guaranteed to succeed
Browse files Browse the repository at this point in the history
Summary:
Back port of: python/cpython#122207 fixing python/cpython#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
  • Loading branch information
DinoV authored and facebook-github-bot committed Jul 24, 2024
1 parent 899af79 commit 4fc0bc3
Showing 1 changed file with 10 additions and 9 deletions.
19 changes: 10 additions & 9 deletions Objects/dictobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -1390,17 +1390,18 @@ 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. */
if (insertion_resize(interp, mp, 1) < 0)
goto Fail;
}

uint64_t new_version = _PyDict_NotifyEvent(
interp, PyDict_EVENT_ADDED, mp, key, value);
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);

Expand Down Expand Up @@ -1482,9 +1483,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);
int lazy_imports = PyLazyImport_CheckExact(value);
PyDictKeysObject *newkeys = new_keys_object(
Expand All @@ -1494,6 +1492,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;
Expand Down Expand Up @@ -3671,15 +3672,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)) {
Expand Down

0 comments on commit 4fc0bc3

Please sign in to comment.