From c00d1e094f750613d694c1eb0689c3f9c87c9361 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Mon, 14 Nov 2022 16:12:48 -0700 Subject: [PATCH 1/4] Move next_dict_keys_version to _PyRuntimeState. --- Include/internal/pycore_dict.h | 4 ++++ Include/internal/pycore_runtime.h | 2 ++ Include/internal/pycore_runtime_init.h | 3 +++ Objects/dictobject.c | 6 ++---- Tools/c-analyzer/cpython/globals-to-fix.tsv | 1 - 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Include/internal/pycore_dict.h b/Include/internal/pycore_dict.h index ae4094a095d879..f6e38e67a45f79 100644 --- a/Include/internal/pycore_dict.h +++ b/Include/internal/pycore_dict.h @@ -17,6 +17,10 @@ extern void _PyDict_Fini(PyInterpreterState *interp); /* other API */ +struct _Py_dict_runtime_state { + uint32_t next_keys_version; +}; + #ifndef WITH_FREELISTS // without freelists # define PyDict_MAXFREELIST 0 diff --git a/Include/internal/pycore_runtime.h b/Include/internal/pycore_runtime.h index f8b988021b9992..42620df795e94b 100644 --- a/Include/internal/pycore_runtime.h +++ b/Include/internal/pycore_runtime.h @@ -9,6 +9,7 @@ extern "C" { #endif #include "pycore_atomic.h" /* _Py_atomic_address */ +#include "pycore_dict.h" // struct _Py_dict_runtime_state #include "pycore_dtoa.h" // struct _dtoa_runtime_state #include "pycore_floatobject.h" // struct _Py_float_runtime_state #include "pycore_gil.h" // struct _gil_runtime_state @@ -151,6 +152,7 @@ typedef struct pyruntimestate { struct _Py_float_runtime_state float_state; struct _Py_unicode_runtime_state unicode_state; + struct _Py_dict_runtime_state dict_state; struct { /* Used to set PyTypeObject.tp_version_tag */ diff --git a/Include/internal/pycore_runtime_init.h b/Include/internal/pycore_runtime_init.h index 37bc54ff96c606..e0d27a7ea1eb88 100644 --- a/Include/internal/pycore_runtime_init.h +++ b/Include/internal/pycore_runtime_init.h @@ -58,6 +58,9 @@ extern "C" { .float_format = _py_float_format_unknown, \ .double_format = _py_float_format_unknown, \ }, \ + .dict_state = { \ + .next_keys_version = 2, \ + }, \ .types = { \ .next_version_tag = 1, \ }, \ diff --git a/Objects/dictobject.c b/Objects/dictobject.c index fc487203869c0e..fe2a409d0d0558 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -5661,17 +5661,15 @@ _PyDictKeys_DecRef(PyDictKeysObject *keys) dictkeys_decref(keys); } -static uint32_t next_dict_keys_version = 2; - uint32_t _PyDictKeys_GetVersionForCurrentState(PyDictKeysObject *dictkeys) { if (dictkeys->dk_version != 0) { return dictkeys->dk_version; } - if (next_dict_keys_version == 0) { + if (_PyRuntime.dict_state.next_keys_version == 0) { return 0; } - uint32_t v = next_dict_keys_version++; + uint32_t v = _PyRuntime.dict_state.next_keys_version++; dictkeys->dk_version = v; return v; } diff --git a/Tools/c-analyzer/cpython/globals-to-fix.tsv b/Tools/c-analyzer/cpython/globals-to-fix.tsv index ee0c85bd425e87..10a872535c11f5 100644 --- a/Tools/c-analyzer/cpython/globals-to-fix.tsv +++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv @@ -329,7 +329,6 @@ Python/suggestions.c levenshtein_distance buffer - # 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 - Python/perf_trampoline.c - perf_status - From a937251749be819c780f530f84b509a2c7c9dc9f Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Mon, 14 Nov 2022 16:38:12 -0700 Subject: [PATCH 2/4] Move _pydict_global_version to _PyRuntimeState. --- Include/internal/pycore_dict.h | 32 +++----------- Include/internal/pycore_dict_state.h | 47 +++++++++++++++++++++ Include/internal/pycore_interp.h | 4 +- Include/internal/pycore_runtime.h | 2 +- Makefile.pre.in | 1 + Modules/_functoolsmodule.c | 1 + Objects/call.c | 1 + Objects/dictobject.c | 15 +++---- Objects/typeobject.c | 1 + PCbuild/pythoncore.vcxproj | 2 + PCbuild/pythoncore.vcxproj.filters | 6 +++ Python/pystate.c | 2 +- Tools/c-analyzer/cpython/globals-to-fix.tsv | 1 - 13 files changed, 72 insertions(+), 43 deletions(-) create mode 100644 Include/internal/pycore_dict_state.h diff --git a/Include/internal/pycore_dict.h b/Include/internal/pycore_dict.h index f6e38e67a45f79..25bd3bffb2e7aa 100644 --- a/Include/internal/pycore_dict.h +++ b/Include/internal/pycore_dict.h @@ -9,6 +9,9 @@ extern "C" { # error "this header requires Py_BUILD_CORE define" #endif +#include "pycore_dict_state.h" +#include "pycore_runtime.h" // _PyRuntime + /* runtime lifecycle */ @@ -17,29 +20,6 @@ extern void _PyDict_Fini(PyInterpreterState *interp); /* other API */ -struct _Py_dict_runtime_state { - uint32_t next_keys_version; -}; - -#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 { /* Cached hash code of me_key. */ Py_hash_t me_hash; @@ -156,13 +136,11 @@ struct _dictvalues { (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) -extern uint64_t _pydict_global_version; - -#define DICT_MAX_WATCHERS 8 #define DICT_VERSION_INCREMENT (1 << DICT_MAX_WATCHERS) #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 _PyDict_SendEvent(int watcher_bits, diff --git a/Include/internal/pycore_dict_state.h b/Include/internal/pycore_dict_state.h new file mode 100644 index 00000000000000..c5cfb7a99455f5 --- /dev/null +++ b/Include/internal/pycore_dict_state.h @@ -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]; + int numfree; + PyDictKeysObject *keys_free_list[PyDict_MAXFREELIST]; + int keys_numfree; +#endif + PyDict_WatchCallback watchers[DICT_MAX_WATCHERS]; +}; + + +#ifdef __cplusplus +} +#endif +#endif /* !Py_INTERNAL_DICT_STATE_H */ diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h index b7fc4b480d7f80..437bf74b771dc0 100644 --- a/Include/internal/pycore_interp.h +++ b/Include/internal/pycore_interp.h @@ -14,7 +14,7 @@ extern "C" { #include "pycore_ast_state.h" // struct ast_state #include "pycore_code.h" // struct callable_cache #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_floatobject.h" // struct _Py_float_state #include "pycore_genobject.h" // struct _Py_async_gen_state @@ -171,8 +171,6 @@ struct _is { // Initialized to _PyEval_EvalFrameDefault(). _PyFrameEvalFunction eval_frame; - PyDict_WatchCallback dict_watchers[DICT_MAX_WATCHERS]; - Py_ssize_t co_extra_user_count; freefunc co_extra_freefuncs[MAX_CO_EXTRA_USERS]; diff --git a/Include/internal/pycore_runtime.h b/Include/internal/pycore_runtime.h index 42620df795e94b..839fc0204b8fc1 100644 --- a/Include/internal/pycore_runtime.h +++ b/Include/internal/pycore_runtime.h @@ -9,7 +9,7 @@ extern "C" { #endif #include "pycore_atomic.h" /* _Py_atomic_address */ -#include "pycore_dict.h" // struct _Py_dict_runtime_state +#include "pycore_dict_state.h" // struct _Py_dict_runtime_state #include "pycore_dtoa.h" // struct _dtoa_runtime_state #include "pycore_floatobject.h" // struct _Py_float_runtime_state #include "pycore_gil.h" // struct _gil_runtime_state diff --git a/Makefile.pre.in b/Makefile.pre.in index e1910f6aab8c14..b7505c3364309b 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1627,6 +1627,7 @@ PYTHON_HEADERS= \ $(srcdir)/Include/internal/pycore_condvar.h \ $(srcdir)/Include/internal/pycore_context.h \ $(srcdir)/Include/internal/pycore_dict.h \ + $(srcdir)/Include/internal/pycore_dict_state.h \ $(srcdir)/Include/internal/pycore_descrobject.h \ $(srcdir)/Include/internal/pycore_dtoa.h \ $(srcdir)/Include/internal/pycore_exceptions.h \ diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 3aef296745ada1..9b30f41ec69932 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -1,5 +1,6 @@ #include "Python.h" #include "pycore_call.h" // _PyObject_CallNoArgs() +#include "pycore_dict.h" // _PyDict_Pop_KnownHash() #include "pycore_long.h" // _PyLong_GetZero() #include "pycore_moduleobject.h" // _PyModule_GetState() #include "pycore_object.h" // _PyObject_GC_TRACK diff --git a/Objects/call.c b/Objects/call.c index c4d31d0a27ece6..bd027e41f8a9a5 100644 --- a/Objects/call.c +++ b/Objects/call.c @@ -1,6 +1,7 @@ #include "Python.h" #include "pycore_call.h" // _PyObject_CallNoArgsTstate() #include "pycore_ceval.h" // _Py_EnterRecursiveCallTstate() +#include "pycore_dict.h" // _PyDict_FromItems() #include "pycore_object.h" // _PyCFunctionWithKeywords_TrampolineCall() #include "pycore_pyerrors.h" // _PyErr_Occurred() #include "pycore_pystate.h" // _PyThreadState_GET() diff --git a/Objects/dictobject.c b/Objects/dictobject.c index fe2a409d0d0558..5c5186dda50a79 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -236,11 +236,6 @@ static int dictresize(PyDictObject *mp, uint8_t log_newsize, int unicode); 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" @@ -5681,7 +5676,7 @@ validate_watcher_id(PyInterpreterState *interp, int watcher_id) PyErr_Format(PyExc_ValueError, "Invalid dict watcher ID %d", watcher_id); 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); return -1; } @@ -5724,8 +5719,8 @@ PyDict_AddWatcher(PyDict_WatchCallback callback) PyInterpreterState *interp = _PyInterpreterState_GET(); for (int i = 0; i < DICT_MAX_WATCHERS; i++) { - if (!interp->dict_watchers[i]) { - interp->dict_watchers[i] = callback; + if (!interp->dict_state.watchers[i]) { + interp->dict_state.watchers[i] = callback; return i; } } @@ -5741,7 +5736,7 @@ PyDict_ClearWatcher(int watcher_id) if (validate_watcher_id(interp, watcher_id)) { return -1; } - interp->dict_watchers[watcher_id] = NULL; + interp->dict_state.watchers[watcher_id] = NULL; return 0; } @@ -5755,7 +5750,7 @@ _PyDict_SendEvent(int watcher_bits, PyInterpreterState *interp = _PyInterpreterState_GET(); for (int i = 0; i < DICT_MAX_WATCHERS; i++) { 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)) { // some dict modification paths (e.g. PyDict_Clear) can't raise, so we // can't propagate exceptions from dict watchers. diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 675d6d874de6bd..12dce422a1c2bb 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -4,6 +4,7 @@ #include "pycore_call.h" #include "pycore_code.h" // CO_FAST_FREE #include "pycore_compile.h" // _Py_Mangle() +#include "pycore_dict.h" // _PyDict_KeysSize() #include "pycore_initconfig.h" // _PyStatus_OK() #include "pycore_moduleobject.h" // _PyModule_GetDef() #include "pycore_object.h" // _PyType_HasFeature() diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index 768f5f1ccc5181..fd5c3175ce423b 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -208,6 +208,8 @@ + + diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters index fd03a7ac450f9d..3ab7f31dff43f3 100644 --- a/PCbuild/pythoncore.vcxproj.filters +++ b/PCbuild/pythoncore.vcxproj.filters @@ -531,6 +531,12 @@ Include\internal + + Include\internal + + + Include\internal + Include\internal diff --git a/Python/pystate.c b/Python/pystate.c index b94fbf6ca0ba0d..1a14ebb39fb93b 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -458,7 +458,7 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate) Py_CLEAR(interp->interpreter_trampoline); 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. diff --git a/Tools/c-analyzer/cpython/globals-to-fix.tsv b/Tools/c-analyzer/cpython/globals-to-fix.tsv index 10a872535c11f5..4e19c42ae57288 100644 --- a/Tools/c-analyzer/cpython/globals-to-fix.tsv +++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv @@ -328,7 +328,6 @@ Objects/unicodeobject.c - ucnhash_capi - Python/suggestions.c levenshtein_distance buffer - # other -Objects/dictobject.c - _pydict_global_version - Objects/funcobject.c - next_func_version - Objects/object.c - _Py_RefTotal - Python/perf_trampoline.c - perf_status - From ba9e889f8b3049ff897a4924747c8f43aa2cde3e Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Mon, 14 Nov 2022 16:49:31 -0700 Subject: [PATCH 3/4] Move next_func_version to _PyRuntimeState. --- Include/internal/pycore_function.h | 4 ++++ Include/internal/pycore_runtime.h | 2 ++ Include/internal/pycore_runtime_init.h | 3 +++ Objects/funcobject.c | 6 ++---- Tools/c-analyzer/cpython/globals-to-fix.tsv | 1 - 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Include/internal/pycore_function.h b/Include/internal/pycore_function.h index 1c87aa31ddb611..c95190f5217315 100644 --- a/Include/internal/pycore_function.h +++ b/Include/internal/pycore_function.h @@ -8,6 +8,10 @@ extern "C" { # error "this header requires Py_BUILD_CORE define" #endif +struct _py_func_runtime_state { + uint32_t next_version; +}; + extern PyFunctionObject* _PyFunction_FromConstructor(PyFrameConstructor *constr); extern uint32_t _PyFunction_GetVersionForCurrentState(PyFunctionObject *func); diff --git a/Include/internal/pycore_runtime.h b/Include/internal/pycore_runtime.h index 839fc0204b8fc1..c1829cb1bdadeb 100644 --- a/Include/internal/pycore_runtime.h +++ b/Include/internal/pycore_runtime.h @@ -12,6 +12,7 @@ extern "C" { #include "pycore_dict_state.h" // struct _Py_dict_runtime_state #include "pycore_dtoa.h" // struct _dtoa_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_global_objects.h" // struct _Py_global_objects #include "pycore_import.h" // struct _import_runtime_state @@ -153,6 +154,7 @@ typedef struct pyruntimestate { struct _Py_float_runtime_state float_state; struct _Py_unicode_runtime_state unicode_state; struct _Py_dict_runtime_state dict_state; + struct _py_func_runtime_state func_state; struct { /* Used to set PyTypeObject.tp_version_tag */ diff --git a/Include/internal/pycore_runtime_init.h b/Include/internal/pycore_runtime_init.h index e0d27a7ea1eb88..69b7f2e5486663 100644 --- a/Include/internal/pycore_runtime_init.h +++ b/Include/internal/pycore_runtime_init.h @@ -61,6 +61,9 @@ extern "C" { .dict_state = { \ .next_keys_version = 2, \ }, \ + .func_state = { \ + .next_version = 1, \ + }, \ .types = { \ .next_version_tag = 1, \ }, \ diff --git a/Objects/funcobject.c b/Objects/funcobject.c index 80117bfb203749..c700a41a909063 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -7,8 +7,6 @@ #include "pycore_pyerrors.h" // _PyErr_Occurred() #include "structmember.h" // PyMemberDef -static uint32_t next_func_version = 1; - PyFunctionObject * _PyFunction_FromConstructor(PyFrameConstructor *constr) { @@ -129,10 +127,10 @@ uint32_t _PyFunction_GetVersionForCurrentState(PyFunctionObject *func) if (func->vectorcall != _PyFunction_Vectorcall) { return 0; } - if (next_func_version == 0) { + if (_PyRuntime.func_state.next_version == 0) { return 0; } - uint32_t v = next_func_version++; + uint32_t v = _PyRuntime.func_state.next_version++; func->func_version = v; return v; } diff --git a/Tools/c-analyzer/cpython/globals-to-fix.tsv b/Tools/c-analyzer/cpython/globals-to-fix.tsv index 4e19c42ae57288..8e595629d5db1d 100644 --- a/Tools/c-analyzer/cpython/globals-to-fix.tsv +++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv @@ -328,7 +328,6 @@ Objects/unicodeobject.c - ucnhash_capi - Python/suggestions.c levenshtein_distance buffer - # other -Objects/funcobject.c - next_func_version - Objects/object.c - _Py_RefTotal - Python/perf_trampoline.c - perf_status - Python/perf_trampoline.c - extra_code_index - From f684dcd7a0f4ae0c31630905e9f81bcd1ac975f9 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Wed, 16 Nov 2022 09:48:46 -0700 Subject: [PATCH 4/4] Make "struct _Py_dict_state" a little more compact. --- Include/internal/pycore_dict_state.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Include/internal/pycore_dict_state.h b/Include/internal/pycore_dict_state.h index c5cfb7a99455f5..77375ea8beb877 100644 --- a/Include/internal/pycore_dict_state.h +++ b/Include/internal/pycore_dict_state.h @@ -33,8 +33,8 @@ 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 numfree; int keys_numfree; #endif PyDict_WatchCallback watchers[DICT_MAX_WATCHERS];