gh-124296: Remove private dictionary version tag (PEP 699) (#124472)

This commit is contained in:
Sam Gross 2024-10-01 12:39:56 -04:00 committed by GitHub
parent 60ff67d010
commit 5aa91c56bf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 35 additions and 354 deletions

View file

@ -877,7 +877,7 @@ new_dict(PyInterpreterState *interp,
mp->ma_keys = keys;
mp->ma_values = values;
mp->ma_used = used;
mp->ma_version_tag = DICT_NEXT_VERSION(interp);
mp->_ma_watcher_tag = 0;
ASSERT_CONSISTENT(mp);
return (PyObject *)mp;
}
@ -1678,8 +1678,7 @@ insert_combined_dict(PyInterpreterState *interp, PyDictObject *mp,
}
}
uint64_t new_version = _PyDict_NotifyEvent(
interp, PyDict_EVENT_ADDED, mp, key, value);
_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);
@ -1698,7 +1697,6 @@ insert_combined_dict(PyInterpreterState *interp, PyDictObject *mp,
STORE_VALUE(ep, value);
STORE_HASH(ep, hash);
}
mp->ma_version_tag = new_version;
STORE_KEYS_USABLE(mp->ma_keys, mp->ma_keys->dk_usable - 1);
STORE_KEYS_NENTRIES(mp->ma_keys, mp->ma_keys->dk_nentries + 1);
assert(mp->ma_keys->dk_usable >= 0);
@ -1744,16 +1742,14 @@ insert_split_value(PyInterpreterState *interp, PyDictObject *mp, PyObject *key,
MAINTAIN_TRACKING(mp, key, value);
PyObject *old_value = mp->ma_values->values[ix];
if (old_value == NULL) {
uint64_t new_version = _PyDict_NotifyEvent(interp, PyDict_EVENT_ADDED, mp, key, value);
_PyDict_NotifyEvent(interp, PyDict_EVENT_ADDED, mp, key, value);
STORE_SPLIT_VALUE(mp, ix, Py_NewRef(value));
_PyDictValues_AddToInsertionOrder(mp->ma_values, ix);
STORE_USED(mp, mp->ma_used + 1);
mp->ma_version_tag = new_version;
}
else {
uint64_t new_version = _PyDict_NotifyEvent(interp, PyDict_EVENT_MODIFIED, mp, key, value);
_PyDict_NotifyEvent(interp, PyDict_EVENT_MODIFIED, mp, key, value);
STORE_SPLIT_VALUE(mp, ix, Py_NewRef(value));
mp->ma_version_tag = new_version;
// old_value should be DECREFed after GC track checking is done, if not, it could raise a segmentation fault,
// when dict only holds the strong reference to value in ep->me_value.
Py_DECREF(old_value);
@ -1815,8 +1811,7 @@ insertdict(PyInterpreterState *interp, PyDictObject *mp,
}
if (old_value != value) {
uint64_t new_version = _PyDict_NotifyEvent(
interp, PyDict_EVENT_MODIFIED, mp, key, value);
_PyDict_NotifyEvent(interp, PyDict_EVENT_MODIFIED, mp, key, value);
assert(old_value != NULL);
assert(!_PyDict_HasSplitTable(mp));
if (DK_IS_UNICODE(mp->ma_keys)) {
@ -1827,7 +1822,6 @@ insertdict(PyInterpreterState *interp, PyDictObject *mp,
PyDictKeyEntry *ep = &DK_ENTRIES(mp->ma_keys)[ix];
STORE_VALUE(ep, value);
}
mp->ma_version_tag = new_version;
}
Py_XDECREF(old_value); /* which **CAN** re-enter (see issue #22653) */
ASSERT_CONSISTENT(mp);
@ -1857,8 +1851,7 @@ 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);
_PyDict_NotifyEvent(interp, PyDict_EVENT_ADDED, mp, key, value);
/* We don't decref Py_EMPTY_KEYS here because it is immortal. */
assert(mp->ma_values == NULL);
@ -1879,7 +1872,6 @@ insert_to_emptydict(PyInterpreterState *interp, PyDictObject *mp,
STORE_VALUE(ep, value);
}
STORE_USED(mp, mp->ma_used + 1);
mp->ma_version_tag = new_version;
newkeys->dk_usable--;
newkeys->dk_nentries++;
// We store the keys last so no one can see them in a partially inconsistent
@ -2612,7 +2604,7 @@ delete_index_from_values(PyDictValues *values, Py_ssize_t ix)
static void
delitem_common(PyDictObject *mp, Py_hash_t hash, Py_ssize_t ix,
PyObject *old_value, uint64_t new_version)
PyObject *old_value)
{
PyObject *old_key;
@ -2622,7 +2614,6 @@ delitem_common(PyDictObject *mp, Py_hash_t hash, Py_ssize_t ix,
assert(hashpos >= 0);
STORE_USED(mp, mp->ma_used - 1);
mp->ma_version_tag = new_version;
if (_PyDict_HasSplitTable(mp)) {
assert(old_value == mp->ma_values->values[ix]);
STORE_SPLIT_VALUE(mp, ix, NULL);
@ -2692,9 +2683,8 @@ delitem_knownhash_lock_held(PyObject *op, PyObject *key, Py_hash_t hash)
}
PyInterpreterState *interp = _PyInterpreterState_GET();
uint64_t new_version = _PyDict_NotifyEvent(
interp, PyDict_EVENT_DELETED, mp, key, NULL);
delitem_common(mp, hash, ix, old_value, new_version);
_PyDict_NotifyEvent(interp, PyDict_EVENT_DELETED, mp, key, NULL);
delitem_common(mp, hash, ix, old_value);
return 0;
}
@ -2740,9 +2730,8 @@ delitemif_lock_held(PyObject *op, PyObject *key,
if (res > 0) {
PyInterpreterState *interp = _PyInterpreterState_GET();
uint64_t new_version = _PyDict_NotifyEvent(
interp, PyDict_EVENT_DELETED, mp, key, NULL);
delitem_common(mp, hash, ix, old_value, new_version);
_PyDict_NotifyEvent(interp, PyDict_EVENT_DELETED, mp, key, NULL);
delitem_common(mp, hash, ix, old_value);
return 1;
} else {
return 0;
@ -2786,11 +2775,9 @@ clear_lock_held(PyObject *op)
}
/* Empty the dict... */
PyInterpreterState *interp = _PyInterpreterState_GET();
uint64_t new_version = _PyDict_NotifyEvent(
interp, PyDict_EVENT_CLEARED, mp, NULL, NULL);
_PyDict_NotifyEvent(interp, PyDict_EVENT_CLEARED, mp, NULL, NULL);
// We don't inc ref empty keys because they're immortal
ensure_shared_on_resize(mp);
mp->ma_version_tag = new_version;
STORE_USED(mp, 0);
if (oldvalues == NULL) {
set_keys(mp, Py_EMPTY_KEYS);
@ -2950,9 +2937,8 @@ _PyDict_Pop_KnownHash(PyDictObject *mp, PyObject *key, Py_hash_t hash,
assert(old_value != NULL);
PyInterpreterState *interp = _PyInterpreterState_GET();
uint64_t new_version = _PyDict_NotifyEvent(
interp, PyDict_EVENT_DELETED, mp, key, NULL);
delitem_common(mp, hash, ix, Py_NewRef(old_value), new_version);
_PyDict_NotifyEvent(interp, PyDict_EVENT_DELETED, mp, key, NULL);
delitem_common(mp, hash, ix, Py_NewRef(old_value));
ASSERT_CONSISTENT(mp);
if (result) {
@ -3717,8 +3703,7 @@ dict_dict_merge(PyInterpreterState *interp, PyDictObject *mp, PyDictObject *othe
(DK_LOG_SIZE(okeys) == PyDict_LOG_MINSIZE ||
USABLE_FRACTION(DK_SIZE(okeys)/2) < other->ma_used)
) {
uint64_t new_version = _PyDict_NotifyEvent(
interp, PyDict_EVENT_CLONED, mp, (PyObject *)other, NULL);
_PyDict_NotifyEvent(interp, PyDict_EVENT_CLONED, mp, (PyObject *)other, NULL);
PyDictKeysObject *keys = clone_combined_dict_keys(other);
if (keys == NULL)
return -1;
@ -3727,7 +3712,6 @@ dict_dict_merge(PyInterpreterState *interp, PyDictObject *mp, PyDictObject *othe
dictkeys_decref(interp, mp->ma_keys, IS_DICT_SHARED(mp));
mp->ma_keys = keys;
STORE_USED(mp, other->ma_used);
mp->ma_version_tag = new_version;
ASSERT_CONSISTENT(mp);
if (_PyObject_GC_IS_TRACKED(other) && !_PyObject_GC_IS_TRACKED(mp)) {
@ -3982,7 +3966,7 @@ copy_lock_held(PyObject *o)
split_copy->ma_values = newvalues;
split_copy->ma_keys = mp->ma_keys;
split_copy->ma_used = mp->ma_used;
split_copy->ma_version_tag = DICT_NEXT_VERSION(interp);
split_copy->_ma_watcher_tag = 0;
dictkeys_incref(mp->ma_keys);
if (_PyObject_GC_IS_TRACKED(mp))
_PyObject_GC_TRACK(split_copy);
@ -4430,7 +4414,6 @@ dict_popitem_impl(PyDictObject *self)
{
Py_ssize_t i, j;
PyObject *res;
uint64_t new_version;
PyInterpreterState *interp = _PyInterpreterState_GET();
ASSERT_DICT_LOCKED(self);
@ -4473,8 +4456,7 @@ dict_popitem_impl(PyDictObject *self)
assert(i >= 0);
key = ep0[i].me_key;
new_version = _PyDict_NotifyEvent(
interp, PyDict_EVENT_DELETED, self, key, NULL);
_PyDict_NotifyEvent(interp, PyDict_EVENT_DELETED, self, key, NULL);
hash = unicode_get_hash(key);
value = ep0[i].me_value;
ep0[i].me_key = NULL;
@ -4489,8 +4471,7 @@ dict_popitem_impl(PyDictObject *self)
assert(i >= 0);
key = ep0[i].me_key;
new_version = _PyDict_NotifyEvent(
interp, PyDict_EVENT_DELETED, self, key, NULL);
_PyDict_NotifyEvent(interp, PyDict_EVENT_DELETED, self, key, NULL);
hash = ep0[i].me_hash;
value = ep0[i].me_value;
ep0[i].me_key = NULL;
@ -4508,7 +4489,6 @@ dict_popitem_impl(PyDictObject *self)
/* We can't dk_usable++ since there is DKIX_DUMMY in indices */
STORE_KEYS_NENTRIES(self->ma_keys, i);
STORE_USED(self, self->ma_used - 1);
self->ma_version_tag = new_version;
ASSERT_CONSISTENT(self);
return res;
}
@ -4759,8 +4739,7 @@ dict_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
PyDictObject *d = (PyDictObject *)self;
d->ma_used = 0;
d->ma_version_tag = DICT_NEXT_VERSION(
_PyInterpreterState_GET());
d->_ma_watcher_tag = 0;
dictkeys_incref(Py_EMPTY_KEYS);
d->ma_keys = Py_EMPTY_KEYS;
d->ma_values = NULL;
@ -7384,7 +7363,7 @@ PyDict_Watch(int watcher_id, PyObject* dict)
if (validate_watcher_id(interp, watcher_id)) {
return -1;
}
((PyDictObject*)dict)->ma_version_tag |= (1LL << watcher_id);
((PyDictObject*)dict)->_ma_watcher_tag |= (1LL << watcher_id);
return 0;
}
@ -7399,7 +7378,7 @@ PyDict_Unwatch(int watcher_id, PyObject* dict)
if (validate_watcher_id(interp, watcher_id)) {
return -1;
}
((PyDictObject*)dict)->ma_version_tag &= ~(1LL << watcher_id);
((PyDictObject*)dict)->_ma_watcher_tag &= ~(1LL << watcher_id);
return 0;
}