Add a new private version to the builtin dict type
Issue #26058: Add a new private version to the builtin dict type, incremented
at each dictionary creation and at each dictionary change.
Implementation of the PEP 509.
diff --git a/Objects/dictobject.c b/Objects/dictobject.c
index 3d5d4bd..d6f1835 100644
--- a/Objects/dictobject.c
+++ b/Objects/dictobject.c
@@ -237,6 +237,13 @@
static int dictresize(PyDictObject *mp, Py_ssize_t minused);
+/* Global counter used to set ma_version_tag field of dictionary.
+ * It is incremented each time that a dictionary is created and each
+ * time that a dictionary is modified. */
+static uint64_t pydict_global_version = 0;
+
+#define DICT_NEXT_VERSION() (++pydict_global_version)
+
/* Dictionary reuse scheme to save calls to malloc and free */
#ifndef PyDict_MAXFREELIST
#define PyDict_MAXFREELIST 80
@@ -511,6 +518,7 @@
mp->ma_keys = keys;
mp->ma_values = values;
mp->ma_used = 0;
+ mp->ma_version_tag = DICT_NEXT_VERSION();
return (PyObject *)mp;
}
@@ -1074,6 +1082,7 @@
ep->me_value = value;
}
mp->ma_used++;
+ mp->ma_version_tag = DICT_NEXT_VERSION();
mp->ma_keys->dk_usable--;
mp->ma_keys->dk_nentries++;
assert(mp->ma_keys->dk_usable >= 0);
@@ -1085,6 +1094,8 @@
old_value = *value_addr;
if (old_value != NULL) {
*value_addr = value;
+ mp->ma_version_tag = DICT_NEXT_VERSION();
+
Py_DECREF(old_value); /* which **CAN** re-enter (see issue #22653) */
return 0;
}
@@ -1094,6 +1105,7 @@
assert(ix == mp->ma_used);
*value_addr = value;
mp->ma_used++;
+ mp->ma_version_tag = DICT_NEXT_VERSION();
return 0;
}
@@ -1533,6 +1545,7 @@
old_value = *value_addr;
*value_addr = NULL;
mp->ma_used--;
+ mp->ma_version_tag = DICT_NEXT_VERSION();
if (_PyDict_HasSplitTable(mp)) {
mp->ma_keys->dk_usable = 0;
}
@@ -1568,6 +1581,7 @@
mp->ma_keys = Py_EMPTY_KEYS;
mp->ma_values = empty_values;
mp->ma_used = 0;
+ mp->ma_version_tag = DICT_NEXT_VERSION();
/* ...then clear the keys and values */
if (oldvalues != NULL) {
n = oldkeys->dk_nentries;
@@ -1706,9 +1720,11 @@
_PyErr_SetKeyError(key);
return NULL;
}
+
old_value = *value_addr;
*value_addr = NULL;
mp->ma_used--;
+ mp->ma_version_tag = DICT_NEXT_VERSION();
if (!_PyDict_HasSplitTable(mp)) {
dk_set_index(mp->ma_keys, hashpos, DKIX_DUMMY);
ep = &DK_ENTRIES(mp->ma_keys)[ix];
@@ -2659,6 +2675,7 @@
mp->ma_keys->dk_usable--;
mp->ma_keys->dk_nentries++;
mp->ma_used++;
+ mp->ma_version_tag = DICT_NEXT_VERSION();
}
else
val = *value_addr;
@@ -2752,6 +2769,7 @@
/* We can't dk_usable++ since there is DKIX_DUMMY in indices */
mp->ma_keys->dk_nentries = i;
mp->ma_used--;
+ mp->ma_version_tag = DICT_NEXT_VERSION();
return res;
}
@@ -2970,6 +2988,7 @@
_PyObject_GC_UNTRACK(d);
d->ma_used = 0;
+ d->ma_version_tag = DICT_NEXT_VERSION();
d->ma_keys = new_keys_object(PyDict_MINSIZE);
if (d->ma_keys == NULL) {
Py_DECREF(self);