diff --git a/Include/internal/pycore_dict.h b/Include/internal/pycore_dict.h
index ae4094a095d879..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,25 +20,6 @@ extern void _PyDict_Fini(PyInterpreterState *interp);
/* 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 {
/* Cached hash code of me_key. */
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]))
#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..77375ea8beb877
--- /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];
+ 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 */
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_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 f8b988021b9992..c1829cb1bdadeb 100644
--- a/Include/internal/pycore_runtime.h
+++ b/Include/internal/pycore_runtime.h
@@ -9,8 +9,10 @@ extern "C" {
#endif
#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_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
@@ -151,6 +153,8 @@ 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 37bc54ff96c606..69b7f2e5486663 100644
--- a/Include/internal/pycore_runtime_init.h
+++ b/Include/internal/pycore_runtime_init.h
@@ -58,6 +58,12 @@ extern "C" {
.float_format = _py_float_format_unknown, \
.double_format = _py_float_format_unknown, \
}, \
+ .dict_state = { \
+ .next_keys_version = 2, \
+ }, \
+ .func_state = { \
+ .next_version = 1, \
+ }, \
.types = { \
.next_version_tag = 1, \
}, \
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 fc487203869c0e..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"
@@ -5661,17 +5656,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;
}
@@ -5683,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;
}
@@ -5726,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;
}
}
@@ -5743,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;
}
@@ -5757,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/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/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 ee0c85bd425e87..8e595629d5db1d 100644
--- a/Tools/c-analyzer/cpython/globals-to-fix.tsv
+++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv
@@ -328,9 +328,6 @@ Objects/unicodeobject.c - ucnhash_capi -
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 -
Python/perf_trampoline.c - extra_code_index -