gh-81057: Move the global Dict-Related Versions to _PyRuntimeState (gh-99497)

We also move the global func version.

https://github.com/python/cpython/issues/81057
This commit is contained in:
Eric Snow 2022-11-16 10:37:29 -07:00 committed by GitHub
parent 8211cf5d28
commit 9db1e17c80
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 89 additions and 48 deletions

View file

@ -9,6 +9,9 @@ extern "C" {
# error "this header requires Py_BUILD_CORE define" # error "this header requires Py_BUILD_CORE define"
#endif #endif
#include "pycore_dict_state.h"
#include "pycore_runtime.h" // _PyRuntime
/* runtime lifecycle */ /* runtime lifecycle */
@ -17,25 +20,6 @@ extern void _PyDict_Fini(PyInterpreterState *interp);
/* other API */ /* other API */
#ifndef WITH_FREELISTS
// without freelists
# define PyDict_MAXFREELIST 0
#endif
#ifndef PyDict_MAXFREELIST
# define PyDict_MAXFREELIST 80
#endif
struct _Py_dict_state {
#if PyDict_MAXFREELIST > 0
/* Dictionary reuse scheme to save calls to malloc and free */
PyDictObject *free_list[PyDict_MAXFREELIST];
int numfree;
PyDictKeysObject *keys_free_list[PyDict_MAXFREELIST];
int keys_numfree;
#endif
};
typedef struct { typedef struct {
/* Cached hash code of me_key. */ /* Cached hash code of me_key. */
Py_hash_t me_hash; Py_hash_t me_hash;
@ -152,13 +136,11 @@ struct _dictvalues {
(PyDictUnicodeEntry*)(&((int8_t*)((dk)->dk_indices))[(size_t)1 << (dk)->dk_log2_index_bytes])) (PyDictUnicodeEntry*)(&((int8_t*)((dk)->dk_indices))[(size_t)1 << (dk)->dk_log2_index_bytes]))
#define DK_IS_UNICODE(dk) ((dk)->dk_kind != DICT_KEYS_GENERAL) #define DK_IS_UNICODE(dk) ((dk)->dk_kind != DICT_KEYS_GENERAL)
extern uint64_t _pydict_global_version;
#define DICT_MAX_WATCHERS 8
#define DICT_VERSION_INCREMENT (1 << DICT_MAX_WATCHERS) #define DICT_VERSION_INCREMENT (1 << DICT_MAX_WATCHERS)
#define DICT_VERSION_MASK (DICT_VERSION_INCREMENT - 1) #define DICT_VERSION_MASK (DICT_VERSION_INCREMENT - 1)
#define DICT_NEXT_VERSION() (_pydict_global_version += DICT_VERSION_INCREMENT) #define DICT_NEXT_VERSION() \
(_PyRuntime.dict_state.global_version += DICT_VERSION_INCREMENT)
void void
_PyDict_SendEvent(int watcher_bits, _PyDict_SendEvent(int watcher_bits,

View file

@ -0,0 +1,47 @@
#ifndef Py_INTERNAL_DICT_STATE_H
#define Py_INTERNAL_DICT_STATE_H
#ifdef __cplusplus
extern "C" {
#endif
#ifndef Py_BUILD_CORE
# error "this header requires Py_BUILD_CORE define"
#endif
struct _Py_dict_runtime_state {
/*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. */
uint64_t global_version;
uint32_t next_keys_version;
};
#ifndef WITH_FREELISTS
// without freelists
# define PyDict_MAXFREELIST 0
#endif
#ifndef PyDict_MAXFREELIST
# define PyDict_MAXFREELIST 80
#endif
#define DICT_MAX_WATCHERS 8
struct _Py_dict_state {
#if PyDict_MAXFREELIST > 0
/* Dictionary reuse scheme to save calls to malloc and free */
PyDictObject *free_list[PyDict_MAXFREELIST];
PyDictKeysObject *keys_free_list[PyDict_MAXFREELIST];
int numfree;
int keys_numfree;
#endif
PyDict_WatchCallback watchers[DICT_MAX_WATCHERS];
};
#ifdef __cplusplus
}
#endif
#endif /* !Py_INTERNAL_DICT_STATE_H */

View file

@ -8,6 +8,10 @@ extern "C" {
# error "this header requires Py_BUILD_CORE define" # error "this header requires Py_BUILD_CORE define"
#endif #endif
struct _py_func_runtime_state {
uint32_t next_version;
};
extern PyFunctionObject* _PyFunction_FromConstructor(PyFrameConstructor *constr); extern PyFunctionObject* _PyFunction_FromConstructor(PyFrameConstructor *constr);
extern uint32_t _PyFunction_GetVersionForCurrentState(PyFunctionObject *func); extern uint32_t _PyFunction_GetVersionForCurrentState(PyFunctionObject *func);

View file

@ -14,7 +14,7 @@ extern "C" {
#include "pycore_ast_state.h" // struct ast_state #include "pycore_ast_state.h" // struct ast_state
#include "pycore_code.h" // struct callable_cache #include "pycore_code.h" // struct callable_cache
#include "pycore_context.h" // struct _Py_context_state #include "pycore_context.h" // struct _Py_context_state
#include "pycore_dict.h" // struct _Py_dict_state #include "pycore_dict_state.h" // struct _Py_dict_state
#include "pycore_exceptions.h" // struct _Py_exc_state #include "pycore_exceptions.h" // struct _Py_exc_state
#include "pycore_floatobject.h" // struct _Py_float_state #include "pycore_floatobject.h" // struct _Py_float_state
#include "pycore_genobject.h" // struct _Py_async_gen_state #include "pycore_genobject.h" // struct _Py_async_gen_state
@ -171,8 +171,6 @@ struct _is {
// Initialized to _PyEval_EvalFrameDefault(). // Initialized to _PyEval_EvalFrameDefault().
_PyFrameEvalFunction eval_frame; _PyFrameEvalFunction eval_frame;
PyDict_WatchCallback dict_watchers[DICT_MAX_WATCHERS];
Py_ssize_t co_extra_user_count; Py_ssize_t co_extra_user_count;
freefunc co_extra_freefuncs[MAX_CO_EXTRA_USERS]; freefunc co_extra_freefuncs[MAX_CO_EXTRA_USERS];

View file

@ -9,8 +9,10 @@ extern "C" {
#endif #endif
#include "pycore_atomic.h" /* _Py_atomic_address */ #include "pycore_atomic.h" /* _Py_atomic_address */
#include "pycore_dict_state.h" // struct _Py_dict_runtime_state
#include "pycore_dtoa.h" // struct _dtoa_runtime_state #include "pycore_dtoa.h" // struct _dtoa_runtime_state
#include "pycore_floatobject.h" // struct _Py_float_runtime_state #include "pycore_floatobject.h" // struct _Py_float_runtime_state
#include "pycore_function.h" // struct _func_runtime_state
#include "pycore_gil.h" // struct _gil_runtime_state #include "pycore_gil.h" // struct _gil_runtime_state
#include "pycore_global_objects.h" // struct _Py_global_objects #include "pycore_global_objects.h" // struct _Py_global_objects
#include "pycore_import.h" // struct _import_runtime_state #include "pycore_import.h" // struct _import_runtime_state
@ -151,6 +153,8 @@ typedef struct pyruntimestate {
struct _Py_float_runtime_state float_state; struct _Py_float_runtime_state float_state;
struct _Py_unicode_runtime_state unicode_state; struct _Py_unicode_runtime_state unicode_state;
struct _Py_dict_runtime_state dict_state;
struct _py_func_runtime_state func_state;
struct { struct {
/* Used to set PyTypeObject.tp_version_tag */ /* Used to set PyTypeObject.tp_version_tag */

View file

@ -58,6 +58,12 @@ extern "C" {
.float_format = _py_float_format_unknown, \ .float_format = _py_float_format_unknown, \
.double_format = _py_float_format_unknown, \ .double_format = _py_float_format_unknown, \
}, \ }, \
.dict_state = { \
.next_keys_version = 2, \
}, \
.func_state = { \
.next_version = 1, \
}, \
.types = { \ .types = { \
.next_version_tag = 1, \ .next_version_tag = 1, \
}, \ }, \

View file

@ -1627,6 +1627,7 @@ PYTHON_HEADERS= \
$(srcdir)/Include/internal/pycore_condvar.h \ $(srcdir)/Include/internal/pycore_condvar.h \
$(srcdir)/Include/internal/pycore_context.h \ $(srcdir)/Include/internal/pycore_context.h \
$(srcdir)/Include/internal/pycore_dict.h \ $(srcdir)/Include/internal/pycore_dict.h \
$(srcdir)/Include/internal/pycore_dict_state.h \
$(srcdir)/Include/internal/pycore_descrobject.h \ $(srcdir)/Include/internal/pycore_descrobject.h \
$(srcdir)/Include/internal/pycore_dtoa.h \ $(srcdir)/Include/internal/pycore_dtoa.h \
$(srcdir)/Include/internal/pycore_exceptions.h \ $(srcdir)/Include/internal/pycore_exceptions.h \

View file

@ -1,5 +1,6 @@
#include "Python.h" #include "Python.h"
#include "pycore_call.h" // _PyObject_CallNoArgs() #include "pycore_call.h" // _PyObject_CallNoArgs()
#include "pycore_dict.h" // _PyDict_Pop_KnownHash()
#include "pycore_long.h" // _PyLong_GetZero() #include "pycore_long.h" // _PyLong_GetZero()
#include "pycore_moduleobject.h" // _PyModule_GetState() #include "pycore_moduleobject.h" // _PyModule_GetState()
#include "pycore_object.h" // _PyObject_GC_TRACK #include "pycore_object.h" // _PyObject_GC_TRACK

View file

@ -1,6 +1,7 @@
#include "Python.h" #include "Python.h"
#include "pycore_call.h" // _PyObject_CallNoArgsTstate() #include "pycore_call.h" // _PyObject_CallNoArgsTstate()
#include "pycore_ceval.h" // _Py_EnterRecursiveCallTstate() #include "pycore_ceval.h" // _Py_EnterRecursiveCallTstate()
#include "pycore_dict.h" // _PyDict_FromItems()
#include "pycore_object.h" // _PyCFunctionWithKeywords_TrampolineCall() #include "pycore_object.h" // _PyCFunctionWithKeywords_TrampolineCall()
#include "pycore_pyerrors.h" // _PyErr_Occurred() #include "pycore_pyerrors.h" // _PyErr_Occurred()
#include "pycore_pystate.h" // _PyThreadState_GET() #include "pycore_pystate.h" // _PyThreadState_GET()

View file

@ -236,11 +236,6 @@ static int dictresize(PyDictObject *mp, uint8_t log_newsize, int unicode);
static PyObject* dict_iter(PyDictObject *dict); static PyObject* dict_iter(PyDictObject *dict);
/*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. */
uint64_t _pydict_global_version = 0;
#include "clinic/dictobject.c.h" #include "clinic/dictobject.c.h"
@ -5658,17 +5653,15 @@ _PyDictKeys_DecRef(PyDictKeysObject *keys)
dictkeys_decref(keys); dictkeys_decref(keys);
} }
static uint32_t next_dict_keys_version = 2;
uint32_t _PyDictKeys_GetVersionForCurrentState(PyDictKeysObject *dictkeys) uint32_t _PyDictKeys_GetVersionForCurrentState(PyDictKeysObject *dictkeys)
{ {
if (dictkeys->dk_version != 0) { if (dictkeys->dk_version != 0) {
return dictkeys->dk_version; return dictkeys->dk_version;
} }
if (next_dict_keys_version == 0) { if (_PyRuntime.dict_state.next_keys_version == 0) {
return 0; return 0;
} }
uint32_t v = next_dict_keys_version++; uint32_t v = _PyRuntime.dict_state.next_keys_version++;
dictkeys->dk_version = v; dictkeys->dk_version = v;
return v; return v;
} }
@ -5680,7 +5673,7 @@ validate_watcher_id(PyInterpreterState *interp, int watcher_id)
PyErr_Format(PyExc_ValueError, "Invalid dict watcher ID %d", watcher_id); PyErr_Format(PyExc_ValueError, "Invalid dict watcher ID %d", watcher_id);
return -1; return -1;
} }
if (!interp->dict_watchers[watcher_id]) { if (!interp->dict_state.watchers[watcher_id]) {
PyErr_Format(PyExc_ValueError, "No dict watcher set for ID %d", watcher_id); PyErr_Format(PyExc_ValueError, "No dict watcher set for ID %d", watcher_id);
return -1; return -1;
} }
@ -5723,8 +5716,8 @@ PyDict_AddWatcher(PyDict_WatchCallback callback)
PyInterpreterState *interp = _PyInterpreterState_GET(); PyInterpreterState *interp = _PyInterpreterState_GET();
for (int i = 0; i < DICT_MAX_WATCHERS; i++) { for (int i = 0; i < DICT_MAX_WATCHERS; i++) {
if (!interp->dict_watchers[i]) { if (!interp->dict_state.watchers[i]) {
interp->dict_watchers[i] = callback; interp->dict_state.watchers[i] = callback;
return i; return i;
} }
} }
@ -5740,7 +5733,7 @@ PyDict_ClearWatcher(int watcher_id)
if (validate_watcher_id(interp, watcher_id)) { if (validate_watcher_id(interp, watcher_id)) {
return -1; return -1;
} }
interp->dict_watchers[watcher_id] = NULL; interp->dict_state.watchers[watcher_id] = NULL;
return 0; return 0;
} }
@ -5754,7 +5747,7 @@ _PyDict_SendEvent(int watcher_bits,
PyInterpreterState *interp = _PyInterpreterState_GET(); PyInterpreterState *interp = _PyInterpreterState_GET();
for (int i = 0; i < DICT_MAX_WATCHERS; i++) { for (int i = 0; i < DICT_MAX_WATCHERS; i++) {
if (watcher_bits & 1) { if (watcher_bits & 1) {
PyDict_WatchCallback cb = interp->dict_watchers[i]; PyDict_WatchCallback cb = interp->dict_state.watchers[i];
if (cb && (cb(event, (PyObject*)mp, key, value) < 0)) { if (cb && (cb(event, (PyObject*)mp, key, value) < 0)) {
// some dict modification paths (e.g. PyDict_Clear) can't raise, so we // some dict modification paths (e.g. PyDict_Clear) can't raise, so we
// can't propagate exceptions from dict watchers. // can't propagate exceptions from dict watchers.

View file

@ -7,8 +7,6 @@
#include "pycore_pyerrors.h" // _PyErr_Occurred() #include "pycore_pyerrors.h" // _PyErr_Occurred()
#include "structmember.h" // PyMemberDef #include "structmember.h" // PyMemberDef
static uint32_t next_func_version = 1;
PyFunctionObject * PyFunctionObject *
_PyFunction_FromConstructor(PyFrameConstructor *constr) _PyFunction_FromConstructor(PyFrameConstructor *constr)
{ {
@ -128,10 +126,10 @@ uint32_t _PyFunction_GetVersionForCurrentState(PyFunctionObject *func)
if (func->vectorcall != _PyFunction_Vectorcall) { if (func->vectorcall != _PyFunction_Vectorcall) {
return 0; return 0;
} }
if (next_func_version == 0) { if (_PyRuntime.func_state.next_version == 0) {
return 0; return 0;
} }
uint32_t v = next_func_version++; uint32_t v = _PyRuntime.func_state.next_version++;
func->func_version = v; func->func_version = v;
return v; return v;
} }

View file

@ -4,6 +4,7 @@
#include "pycore_call.h" #include "pycore_call.h"
#include "pycore_code.h" // CO_FAST_FREE #include "pycore_code.h" // CO_FAST_FREE
#include "pycore_compile.h" // _Py_Mangle() #include "pycore_compile.h" // _Py_Mangle()
#include "pycore_dict.h" // _PyDict_KeysSize()
#include "pycore_initconfig.h" // _PyStatus_OK() #include "pycore_initconfig.h" // _PyStatus_OK()
#include "pycore_moduleobject.h" // _PyModule_GetDef() #include "pycore_moduleobject.h" // _PyModule_GetDef()
#include "pycore_object.h" // _PyType_HasFeature() #include "pycore_object.h" // _PyType_HasFeature()

View file

@ -208,6 +208,8 @@
<ClInclude Include="..\Include\internal\pycore_condvar.h" /> <ClInclude Include="..\Include\internal\pycore_condvar.h" />
<ClInclude Include="..\Include\internal\pycore_context.h" /> <ClInclude Include="..\Include\internal\pycore_context.h" />
<ClInclude Include="..\Include\internal\pycore_descrobject.h" /> <ClInclude Include="..\Include\internal\pycore_descrobject.h" />
<ClInclude Include="..\Include\internal\pycore_dict.h" />
<ClInclude Include="..\Include\internal\pycore_dict_state.h" />
<ClInclude Include="..\Include\internal\pycore_dtoa.h" /> <ClInclude Include="..\Include\internal\pycore_dtoa.h" />
<ClInclude Include="..\Include\internal\pycore_exceptions.h" /> <ClInclude Include="..\Include\internal\pycore_exceptions.h" />
<ClInclude Include="..\Include\internal\pycore_fileutils.h" /> <ClInclude Include="..\Include\internal\pycore_fileutils.h" />

View file

@ -531,6 +531,12 @@
<ClInclude Include="..\Include\internal\pycore_descrobject.h"> <ClInclude Include="..\Include\internal\pycore_descrobject.h">
<Filter>Include\internal</Filter> <Filter>Include\internal</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\Include\internal\pycore_dict.h">
<Filter>Include\internal</Filter>
</ClInclude>
<ClInclude Include="..\Include\internal\pycore_dict_state.h">
<Filter>Include\internal</Filter>
</ClInclude>
<ClInclude Include="..\Include\internal\pycore_dtoa.h"> <ClInclude Include="..\Include\internal\pycore_dtoa.h">
<Filter>Include\internal</Filter> <Filter>Include\internal</Filter>
</ClInclude> </ClInclude>

View file

@ -458,7 +458,7 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate)
Py_CLEAR(interp->interpreter_trampoline); Py_CLEAR(interp->interpreter_trampoline);
for (int i=0; i < DICT_MAX_WATCHERS; i++) { for (int i=0; i < DICT_MAX_WATCHERS; i++) {
interp->dict_watchers[i] = NULL; interp->dict_state.watchers[i] = NULL;
} }
// XXX Once we have one allocator per interpreter (i.e. // XXX Once we have one allocator per interpreter (i.e.

View file

@ -320,9 +320,6 @@ Objects/unicodeobject.c - ucnhash_capi -
Python/suggestions.c levenshtein_distance buffer - Python/suggestions.c levenshtein_distance buffer -
# other # other
Objects/dictobject.c - _pydict_global_version -
Objects/dictobject.c - next_dict_keys_version -
Objects/funcobject.c - next_func_version -
Objects/object.c - _Py_RefTotal - Objects/object.c - _Py_RefTotal -
Python/perf_trampoline.c - perf_status - Python/perf_trampoline.c - perf_status -
Python/perf_trampoline.c - extra_code_index - Python/perf_trampoline.c - extra_code_index -

Can't render this file because it has a wrong number of fields in line 4.