Skip to content
5 changes: 5 additions & 0 deletions Include/internal/pycore_dict_state.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ struct _Py_dict_state {
PyDict_WatchCallback watchers[DICT_MAX_WATCHERS];
};

#define _dict_state_INIT \
{ \
.next_keys_version = 2, \
}


#ifdef __cplusplus
}
Expand Down
4 changes: 1 addition & 3 deletions Include/internal/pycore_runtime_init.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,7 @@ extern PyTypeObject _PyExc_MemoryError;
}, \
}, \
.dtoa = _dtoa_state_INIT(&(INTERP)), \
.dict_state = { \
.next_keys_version = 2, \
}, \
.dict_state = _dict_state_INIT, \
.func_state = { \
.next_version = 1, \
}, \
Expand Down
25 changes: 17 additions & 8 deletions Objects/dictobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -300,9 +300,18 @@ _PyDict_DebugMallocStats(FILE *out)

static void free_keys_object(PyInterpreterState *interp, PyDictKeysObject *keys);

/* We might consider modifying PyDictKeysObject so we could use
Py_INCREF() instead of dictkeys_incref() (and likewise with
Py_DECREF() and dictkeys_decref()). However, there doesn't
seem to be enough benefit (performance or otherwise) to justify
the change. */

static inline void
dictkeys_incref(PyDictKeysObject *dk)
{
if (dk->dk_refcnt == _Py_IMMORTAL_REFCNT) {
return;
}
#ifdef Py_REF_DEBUG
_Py_IncRefTotal(_PyInterpreterState_GET());
#endif
Expand All @@ -312,6 +321,9 @@ dictkeys_incref(PyDictKeysObject *dk)
static inline void
dictkeys_decref(PyInterpreterState *interp, PyDictKeysObject *dk)
{
if (dk->dk_refcnt == _Py_IMMORTAL_REFCNT) {
return;
}
assert(dk->dk_refcnt > 0);
#ifdef Py_REF_DEBUG
_Py_DecRefTotal(_PyInterpreterState_GET());
Expand Down Expand Up @@ -447,7 +459,7 @@ estimate_log2_keysize(Py_ssize_t n)
* (which cannot fail and thus can do no allocation).
*/
static PyDictKeysObject empty_keys_struct = {
1, /* dk_refcnt */
_Py_IMMORTAL_REFCNT, /* dk_refcnt */
0, /* dk_log2_size */
0, /* dk_log2_index_bytes */
DICT_KEYS_UNICODE, /* dk_kind */
Expand Down Expand Up @@ -779,6 +791,7 @@ clone_combined_dict_keys(PyDictObject *orig)
assert(PyDict_Check(orig));
assert(Py_TYPE(orig)->tp_iter == (getiterfunc)dict_iter);
assert(orig->ma_values == NULL);
assert(orig->ma_keys != Py_EMPTY_KEYS);
assert(orig->ma_keys->dk_refcnt == 1);

size_t keys_size = _PyDict_KeysSize(orig->ma_keys);
Expand Down Expand Up @@ -1532,11 +1545,7 @@ dictresize(PyInterpreterState *interp, PyDictObject *mp,
#ifdef Py_REF_DEBUG
_Py_DecRefTotal(_PyInterpreterState_GET());
#endif
if (oldkeys == Py_EMPTY_KEYS) {
oldkeys->dk_refcnt--;
assert(oldkeys->dk_refcnt > 0);
}
else {
if (oldkeys != Py_EMPTY_KEYS) {
assert(oldkeys->dk_kind != DICT_KEYS_SPLIT);
assert(oldkeys->dk_refcnt == 1);
#if PyDict_MAXFREELIST > 0
Expand Down Expand Up @@ -2080,8 +2089,8 @@ PyDict_Clear(PyObject *op)
dictkeys_decref(interp, oldkeys);
}
else {
assert(oldkeys->dk_refcnt == 1);
dictkeys_decref(interp, oldkeys);
assert(oldkeys->dk_refcnt == 1 || oldkeys == Py_EMPTY_KEYS);
dictkeys_decref(interp, oldkeys);
}
ASSERT_CONSISTENT(mp);
}
Expand Down