From 8561efcddb8114972c0fd0fbece19a3f7f98adc9 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Sat, 20 Jan 2024 01:43:52 +0900 Subject: [PATCH 01/22] gh-111968: Use per-thread freelists for dict in free-threading --- Include/internal/pycore_dict.h | 3 +- Include/internal/pycore_dict_state.h | 50 ---------------- Include/internal/pycore_freelist.h | 28 +++++++++ Include/internal/pycore_gc.h | 2 +- Include/internal/pycore_interp.h | 2 +- Makefile.pre.in | 1 - Objects/dictobject.c | 85 +++++++++++----------------- PCbuild/pythoncore.vcxproj | 1 - PCbuild/pythoncore.vcxproj.filters | 3 - Python/gc_free_threading.c | 2 - Python/gc_gil.c | 2 - Python/pylifecycle.c | 2 +- Python/pystate.c | 1 + 13 files changed, 67 insertions(+), 115 deletions(-) delete mode 100644 Include/internal/pycore_dict_state.h diff --git a/Include/internal/pycore_dict.h b/Include/internal/pycore_dict.h index d96870e9197bbf..3643b31ea46c49 100644 --- a/Include/internal/pycore_dict.h +++ b/Include/internal/pycore_dict.h @@ -9,6 +9,7 @@ extern "C" { # error "this header requires Py_BUILD_CORE define" #endif +#include "pycore_freelist.h" // _PyFreeListState #include "pycore_identifier.h" // _Py_Identifier #include "pycore_object.h" // PyDictOrValues @@ -69,7 +70,7 @@ extern PyObject* _PyDictView_Intersect(PyObject* self, PyObject *other); /* runtime lifecycle */ -extern void _PyDict_Fini(PyInterpreterState *interp); +extern void _PyDict_Fini(_PyFreeListState *state); /* other API */ diff --git a/Include/internal/pycore_dict_state.h b/Include/internal/pycore_dict_state.h deleted file mode 100644 index ece0f10ca25170..00000000000000 --- a/Include/internal/pycore_dict_state.h +++ /dev/null @@ -1,50 +0,0 @@ -#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 - - -#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 { - /*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; - -#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]; -}; - -#define _dict_state_INIT \ - { \ - .next_keys_version = 2, \ - } - - -#ifdef __cplusplus -} -#endif -#endif /* !Py_INTERNAL_DICT_STATE_H */ diff --git a/Include/internal/pycore_freelist.h b/Include/internal/pycore_freelist.h index 4ab93ee2bf6c32..1870548cb312c0 100644 --- a/Include/internal/pycore_freelist.h +++ b/Include/internal/pycore_freelist.h @@ -17,6 +17,7 @@ extern "C" { # define PyTuple_NFREELISTS PyTuple_MAXSAVESIZE # define PyTuple_MAXFREELIST 2000 # define PyList_MAXFREELIST 80 +# define PyDict_MAXFREELIST 80 # define PyFloat_MAXFREELIST 100 # define PyContext_MAXFREELIST 255 # define _PyAsyncGen_MAXFREELIST 80 @@ -24,6 +25,7 @@ extern "C" { # define PyTuple_NFREELISTS 0 # define PyTuple_MAXFREELIST 0 # define PyList_MAXFREELIST 0 +# define PyDict_MAXFREELIST 0 # define PyFloat_MAXFREELIST 0 # define PyContext_MAXFREELIST 0 # define _PyAsyncGen_MAXFREELIST 0 @@ -63,6 +65,31 @@ struct _Py_float_state { #endif }; +#define DICT_MAX_WATCHERS 8 + +struct _Py_dict_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; + +#ifdef WITH_FREELISTS + /* 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]; +}; + +#define _dict_state_INIT \ + { \ + .next_keys_version = 2, \ + } + struct _Py_slice_state { #ifdef WITH_FREELISTS /* Using a cache is very effective since typically only a single slice is @@ -97,6 +124,7 @@ typedef struct _Py_freelist_state { struct _Py_float_state float_state; struct _Py_tuple_state tuple_state; struct _Py_list_state list_state; + struct _Py_dict_state dict_state; struct _Py_slice_state slice_state; struct _Py_context_state context_state; struct _Py_async_gen_state async_gen_state; diff --git a/Include/internal/pycore_gc.h b/Include/internal/pycore_gc.h index d53de97709a782..355ee3c9ad4fd6 100644 --- a/Include/internal/pycore_gc.h +++ b/Include/internal/pycore_gc.h @@ -250,7 +250,7 @@ extern void _PyTuple_ClearFreeList(_PyFreeListState *state, int is_finalization) extern void _PyFloat_ClearFreeList(_PyFreeListState *state, int is_finalization); extern void _PyList_ClearFreeList(_PyFreeListState *state, int is_finalization); extern void _PySlice_ClearCache(_PyFreeListState *state); -extern void _PyDict_ClearFreeList(PyInterpreterState *interp); +extern void _PyDict_ClearFreeList(_PyFreeListState *state, int is_finalization); extern void _PyAsyncGen_ClearFreeLists(_PyFreeListState *state, int is_finalization); extern void _PyContext_ClearFreeList(_PyFreeListState *state, int is_finalization); extern void _Py_ScheduleGC(PyInterpreterState *interp); diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h index 922c84543a1393..f8084d3645f936 100644 --- a/Include/internal/pycore_interp.h +++ b/Include/internal/pycore_interp.h @@ -16,10 +16,10 @@ extern "C" { #include "pycore_code.h" // struct callable_cache #include "pycore_context.h" // struct _Py_context_state #include "pycore_crossinterp.h" // struct _xidregistry -#include "pycore_dict_state.h" // struct _Py_dict_state #include "pycore_dtoa.h" // struct _dtoa_state #include "pycore_exceptions.h" // struct _Py_exc_state #include "pycore_floatobject.h" // struct _Py_float_state +#include "pycore_freelist.h" // struct _Py_freelist_state #include "pycore_function.h" // FUNC_MAX_WATCHERS #include "pycore_gc.h" // struct _gc_runtime_state #include "pycore_genobject.h" // struct _Py_async_gen_state diff --git a/Makefile.pre.in b/Makefile.pre.in index d251e7c481b52b..e5a2639a5f5c37 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1821,7 +1821,6 @@ PYTHON_HEADERS= \ $(srcdir)/Include/internal/pycore_critical_section.h \ $(srcdir)/Include/internal/pycore_crossinterp.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/Objects/dictobject.c b/Objects/dictobject.c index 2482a918ba983b..d97bae5c067222 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -118,6 +118,7 @@ As a consequence of this, split keys have a maximum size of 16. #include "pycore_ceval.h" // _PyEval_GetBuiltin() #include "pycore_code.h" // stats #include "pycore_dict.h" // export _PyDict_SizeOf() +#include "pycore_freelist.h" // _PyFreeListState_GET() #include "pycore_gc.h" // _PyObject_GC_IS_TRACKED() #include "pycore_object.h" // _PyObject_GC_TRACK(), _PyDebugAllocatorStats() #include "pycore_pyerrors.h" // _PyErr_GetRaisedException() @@ -242,41 +243,40 @@ static PyObject* dict_iter(PyObject *dict); #include "clinic/dictobject.c.h" -#if PyDict_MAXFREELIST > 0 +#ifdef WITH_FREELISTS static struct _Py_dict_state * -get_dict_state(PyInterpreterState *interp) +get_dict_state(void) { - return &interp->dict_state; + _PyFreeListState *state = _PyFreeListState_GET(); + return &state->dict_state; } #endif void -_PyDict_ClearFreeList(PyInterpreterState *interp) +_PyDict_ClearFreeList(_PyFreeListState *freelist_state, int is_finalization) { -#if PyDict_MAXFREELIST > 0 - struct _Py_dict_state *state = &interp->dict_state; - while (state->numfree) { +#ifdef WITH_FREELISTS + struct _Py_dict_state *state = &freelist_state->dict_state; + while (state->numfree > 0) { PyDictObject *op = state->free_list[--state->numfree]; assert(PyDict_CheckExact(op)); PyObject_GC_Del(op); } - while (state->keys_numfree) { + while (state->keys_numfree > 0) { PyObject_Free(state->keys_free_list[--state->keys_numfree]); } + if (is_finalization) { + state->numfree = -1; + state->keys_numfree = -1; + } #endif } - void -_PyDict_Fini(PyInterpreterState *interp) +_PyDict_Fini(_PyFreeListState *freelist_state) { - _PyDict_ClearFreeList(interp); -#if defined(Py_DEBUG) && PyDict_MAXFREELIST > 0 - struct _Py_dict_state *state = &interp->dict_state; - state->numfree = -1; - state->keys_numfree = -1; -#endif + _PyDict_ClearFreeList(freelist_state, 1); } static inline Py_hash_t @@ -290,9 +290,8 @@ unicode_get_hash(PyObject *o) void _PyDict_DebugMallocStats(FILE *out) { -#if PyDict_MAXFREELIST > 0 - PyInterpreterState *interp = _PyInterpreterState_GET(); - struct _Py_dict_state *state = get_dict_state(interp); +#ifdef WITH_FREELISTS + struct _Py_dict_state *state = get_dict_state(); _PyDebugAllocatorStats(out, "free PyDictObject", state->numfree, sizeof(PyDictObject)); #endif @@ -627,12 +626,8 @@ new_keys_object(PyInterpreterState *interp, uint8_t log2_size, bool unicode) log2_bytes = log2_size + 2; } -#if PyDict_MAXFREELIST > 0 - struct _Py_dict_state *state = get_dict_state(interp); -#ifdef Py_DEBUG - // new_keys_object() must not be called after _PyDict_Fini() - assert(state->keys_numfree != -1); -#endif +#ifdef WITH_FREELISTS + struct _Py_dict_state *state = get_dict_state(); if (log2_size == PyDict_LOG_MINSIZE && unicode && state->keys_numfree > 0) { dk = state->keys_free_list[--state->keys_numfree]; OBJECT_STAT_INC(from_freelist); @@ -683,14 +678,11 @@ free_keys_object(PyInterpreterState *interp, PyDictKeysObject *keys) Py_XDECREF(entries[i].me_value); } } -#if PyDict_MAXFREELIST > 0 - struct _Py_dict_state *state = get_dict_state(interp); -#ifdef Py_DEBUG - // free_keys_object() must not be called after _PyDict_Fini() - assert(state->keys_numfree != -1); -#endif +#ifdef WITH_FREELISTS + struct _Py_dict_state *state = get_dict_state(); if (DK_LOG_SIZE(keys) == PyDict_LOG_MINSIZE && state->keys_numfree < PyDict_MAXFREELIST + && state->keys_numfree >= 0 && DK_IS_UNICODE(keys)) { state->keys_free_list[state->keys_numfree++] = keys; OBJECT_STAT_INC(to_freelist); @@ -731,13 +723,9 @@ new_dict(PyInterpreterState *interp, { PyDictObject *mp; assert(keys != NULL); -#if PyDict_MAXFREELIST > 0 - struct _Py_dict_state *state = get_dict_state(interp); -#ifdef Py_DEBUG - // new_dict() must not be called after _PyDict_Fini() - assert(state->numfree != -1); -#endif - if (state->numfree) { +#ifdef WITH_FREELISTS + struct _Py_dict_state *state = get_dict_state(); + if (state->numfree > 0) { mp = state->free_list[--state->numfree]; assert (mp != NULL); assert (Py_IS_TYPE(mp, &PyDict_Type)); @@ -1552,15 +1540,12 @@ dictresize(PyInterpreterState *interp, PyDictObject *mp, #endif assert(oldkeys->dk_kind != DICT_KEYS_SPLIT); assert(oldkeys->dk_refcnt == 1); -#if PyDict_MAXFREELIST > 0 - struct _Py_dict_state *state = get_dict_state(interp); -#ifdef Py_DEBUG - // dictresize() must not be called after _PyDict_Fini() - assert(state->keys_numfree != -1); -#endif +#ifdef WITH_FREELISTS + struct _Py_dict_state *state = get_dict_state(); if (DK_LOG_SIZE(oldkeys) == PyDict_LOG_MINSIZE && DK_IS_UNICODE(oldkeys) && - state->keys_numfree < PyDict_MAXFREELIST) + state->keys_numfree < PyDict_MAXFREELIST && + state->keys_numfree >= 0) { state->keys_free_list[state->keys_numfree++] = oldkeys; OBJECT_STAT_INC(to_freelist); @@ -2480,13 +2465,9 @@ dict_dealloc(PyObject *self) assert(keys->dk_refcnt == 1 || keys == Py_EMPTY_KEYS); dictkeys_decref(interp, keys); } -#if PyDict_MAXFREELIST > 0 - struct _Py_dict_state *state = get_dict_state(interp); -#ifdef Py_DEBUG - // new_dict() must not be called after _PyDict_Fini() - assert(state->numfree != -1); -#endif - if (state->numfree < PyDict_MAXFREELIST && Py_IS_TYPE(mp, &PyDict_Type)) { +#ifdef WITH_FREELISTS + struct _Py_dict_state *state = get_dict_state(); + if (state->numfree < PyDict_MAXFREELIST && state->numfree >=0 && Py_IS_TYPE(mp, &PyDict_Type)) { state->free_list[state->numfree++] = mp; OBJECT_STAT_INC(to_freelist); } diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index 64738b1bbf235d..1860286fcd6195 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -222,7 +222,6 @@ - diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters index b37ca2dfed55ab..199e972cadeb51 100644 --- a/PCbuild/pythoncore.vcxproj.filters +++ b/PCbuild/pythoncore.vcxproj.filters @@ -594,9 +594,6 @@ Include\internal - - Include\internal - Include\internal diff --git a/Python/gc_free_threading.c b/Python/gc_free_threading.c index 207a43b68d21f5..26ea912c99dec8 100644 --- a/Python/gc_free_threading.c +++ b/Python/gc_free_threading.c @@ -14,8 +14,6 @@ void _PyGC_ClearAllFreeLists(PyInterpreterState *interp) { - _PyDict_ClearFreeList(interp); - HEAD_LOCK(&_PyRuntime); _PyThreadStateImpl *tstate = (_PyThreadStateImpl *)interp->threads.head; while (tstate != NULL) { diff --git a/Python/gc_gil.c b/Python/gc_gil.c index 04c1c184250c60..4e2aa8f7af746c 100644 --- a/Python/gc_gil.c +++ b/Python/gc_gil.c @@ -11,8 +11,6 @@ void _PyGC_ClearAllFreeLists(PyInterpreterState *interp) { - _PyDict_ClearFreeList(interp); - _Py_ClearFreeLists(&interp->freelist_state, 0); } diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 0d5eec06e9b458..45896f68e988b5 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -1749,12 +1749,12 @@ finalize_interp_types(PyInterpreterState *interp) // a dict internally. _PyUnicode_ClearInterned(interp); - _PyDict_Fini(interp); _PyUnicode_Fini(interp); _PyFreeListState *state = _PyFreeListState_GET(); _PyTuple_Fini(state); _PyList_Fini(state); + _PyDict_Fini(state); _PyFloat_Fini(state); _PySlice_Fini(state); _PyContext_Fini(state); diff --git a/Python/pystate.c b/Python/pystate.c index 999976283da675..3f1f63ce38a386 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -1461,6 +1461,7 @@ _Py_ClearFreeLists(_PyFreeListState *state, int is_finalization) _PyFloat_ClearFreeList(state, is_finalization); _PyTuple_ClearFreeList(state, is_finalization); _PyList_ClearFreeList(state, is_finalization); + _PyDict_ClearFreeList(state, is_finalization); _PyContext_ClearFreeList(state, is_finalization); _PyAsyncGen_ClearFreeLists(state, is_finalization); } From 6a69d6ffa0e440799b6e977d94b016f9357add2b Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Sat, 20 Jan 2024 02:47:16 +0900 Subject: [PATCH 02/22] Address code review --- Include/internal/pycore_dict.h | 2 +- Include/internal/pycore_freelist.h | 13 ----- Include/internal/pycore_global_dict_state.h | 31 +++++++++++ Include/internal/pycore_interp.h | 58 ++++++++++----------- Include/internal/pycore_runtime_init.h | 2 +- Makefile.pre.in | 1 + Objects/dictobject.c | 14 ++--- PCbuild/pythoncore.vcxproj | 1 + PCbuild/pythoncore.vcxproj.filters | 3 ++ Python/pystate.c | 2 +- 10 files changed, 75 insertions(+), 52 deletions(-) create mode 100644 Include/internal/pycore_global_dict_state.h diff --git a/Include/internal/pycore_dict.h b/Include/internal/pycore_dict.h index 3643b31ea46c49..f2fad22bc872d2 100644 --- a/Include/internal/pycore_dict.h +++ b/Include/internal/pycore_dict.h @@ -211,7 +211,7 @@ static inline PyDictUnicodeEntry* DK_UNICODE_ENTRIES(PyDictKeysObject *dk) { #define DICT_VERSION_MASK (DICT_VERSION_INCREMENT - 1) #define DICT_NEXT_VERSION(INTERP) \ - ((INTERP)->dict_state.global_version += DICT_VERSION_INCREMENT) + ((INTERP)->global_dict_state.global_version += DICT_VERSION_INCREMENT) void _PyDict_SendEvent(int watcher_bits, diff --git a/Include/internal/pycore_freelist.h b/Include/internal/pycore_freelist.h index 1870548cb312c0..44b3f8a93fadb0 100644 --- a/Include/internal/pycore_freelist.h +++ b/Include/internal/pycore_freelist.h @@ -68,12 +68,6 @@ struct _Py_float_state { #define DICT_MAX_WATCHERS 8 struct _Py_dict_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; - #ifdef WITH_FREELISTS /* Dictionary reuse scheme to save calls to malloc and free */ PyDictObject *free_list[PyDict_MAXFREELIST]; @@ -81,15 +75,8 @@ struct _Py_dict_state { int numfree; int keys_numfree; #endif - - PyDict_WatchCallback watchers[DICT_MAX_WATCHERS]; }; -#define _dict_state_INIT \ - { \ - .next_keys_version = 2, \ - } - struct _Py_slice_state { #ifdef WITH_FREELISTS /* Using a cache is very effective since typically only a single slice is diff --git a/Include/internal/pycore_global_dict_state.h b/Include/internal/pycore_global_dict_state.h new file mode 100644 index 00000000000000..da4c9b0c7d9edc --- /dev/null +++ b/Include/internal/pycore_global_dict_state.h @@ -0,0 +1,31 @@ +#ifndef Py_INTERNAL_DICT_GLOBAL_STATE_H +#define Py_INTERNAL_DICT_GLOBAL_STATE_H +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef Py_BUILD_CORE +# error "this header requires Py_BUILD_CORE define" +#endif + +#define DICT_MAX_WATCHERS 8 + +struct _Py_global_dict_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; + PyDict_WatchCallback watchers[DICT_MAX_WATCHERS]; +}; + +#define _dict_state_INIT \ + { \ + .next_keys_version = 2, \ + } + + +#ifdef __cplusplus +} +#endif +#endif /* !Py_INTERNAL_DICT_GLOBAL_STATE_H */ \ No newline at end of file diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h index f8084d3645f936..663d5055fb2ff4 100644 --- a/Include/internal/pycore_interp.h +++ b/Include/internal/pycore_interp.h @@ -8,33 +8,34 @@ extern "C" { # error "this header requires Py_BUILD_CORE define" #endif -#include // bool - -#include "pycore_ast_state.h" // struct ast_state -#include "pycore_atexit.h" // struct atexit_state -#include "pycore_ceval_state.h" // struct _ceval_state -#include "pycore_code.h" // struct callable_cache -#include "pycore_context.h" // struct _Py_context_state -#include "pycore_crossinterp.h" // struct _xidregistry -#include "pycore_dtoa.h" // struct _dtoa_state -#include "pycore_exceptions.h" // struct _Py_exc_state -#include "pycore_floatobject.h" // struct _Py_float_state -#include "pycore_freelist.h" // struct _Py_freelist_state -#include "pycore_function.h" // FUNC_MAX_WATCHERS -#include "pycore_gc.h" // struct _gc_runtime_state -#include "pycore_genobject.h" // struct _Py_async_gen_state -#include "pycore_global_objects.h"// struct _Py_interp_cached_objects -#include "pycore_import.h" // struct _import_state -#include "pycore_instruments.h" // _PY_MONITORING_EVENTS -#include "pycore_list.h" // struct _Py_list_state -#include "pycore_mimalloc.h" // struct _mimalloc_interp_state -#include "pycore_object_state.h" // struct _py_object_state -#include "pycore_obmalloc.h" // struct _obmalloc_state -#include "pycore_tstate.h" // _PyThreadStateImpl -#include "pycore_tuple.h" // struct _Py_tuple_state -#include "pycore_typeobject.h" // struct types_state -#include "pycore_unicodeobject.h" // struct _Py_unicode_state -#include "pycore_warnings.h" // struct _warnings_runtime_state +#include // bool + +#include "pycore_ast_state.h" // struct ast_state +#include "pycore_atexit.h" // struct atexit_state +#include "pycore_ceval_state.h" // struct _ceval_state +#include "pycore_code.h" // struct callable_cache +#include "pycore_context.h" // struct _Py_context_state +#include "pycore_crossinterp.h" // struct _xidregistry +#include "pycore_dtoa.h" // struct _dtoa_state +#include "pycore_exceptions.h" // struct _Py_exc_state +#include "pycore_floatobject.h" // struct _Py_float_state +#include "pycore_freelist.h" // struct _Py_freelist_state +#include "pycore_function.h" // FUNC_MAX_WATCHERS +#include "pycore_gc.h" // struct _gc_runtime_state +#include "pycore_genobject.h" // struct _Py_async_gen_state +#include "pycore_global_dict_state.h" // struct _Py_global_dict_state +#include "pycore_global_objects.h" // struct _Py_interp_cached_objects +#include "pycore_import.h" // struct _import_state +#include "pycore_instruments.h" // _PY_MONITORING_EVENTS +#include "pycore_list.h" // struct _Py_list_state +#include "pycore_mimalloc.h" // struct _mimalloc_interp_state +#include "pycore_object_state.h" // struct _py_object_state +#include "pycore_obmalloc.h" // struct _obmalloc_state +#include "pycore_tstate.h" // _PyThreadStateImpl +#include "pycore_tuple.h" // struct _Py_tuple_state +#include "pycore_typeobject.h" // struct types_state +#include "pycore_unicodeobject.h" // struct _Py_unicode_state +#include "pycore_warnings.h" // struct _warnings_runtime_state struct _Py_long_state { @@ -188,8 +189,7 @@ struct _is { struct _dtoa_state dtoa; struct _py_func_state func_state; - struct _Py_tuple_state tuple; - struct _Py_dict_state dict_state; + struct _Py_global_dict_state global_dict_state; struct _Py_exc_state exc_state; struct ast_state ast; diff --git a/Include/internal/pycore_runtime_init.h b/Include/internal/pycore_runtime_init.h index 5f47d60de37825..e6de0cdde72dd2 100644 --- a/Include/internal/pycore_runtime_init.h +++ b/Include/internal/pycore_runtime_init.h @@ -167,7 +167,7 @@ extern PyTypeObject _PyExc_MemoryError; }, \ .object_state = _py_object_state_INIT(INTERP), \ .dtoa = _dtoa_state_INIT(&(INTERP)), \ - .dict_state = _dict_state_INIT, \ + .global_dict_state = _dict_state_INIT, \ .func_state = { \ .next_version = 1, \ }, \ diff --git a/Makefile.pre.in b/Makefile.pre.in index e5a2639a5f5c37..9168fea98b6a11 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1834,6 +1834,7 @@ PYTHON_HEADERS= \ $(srcdir)/Include/internal/pycore_genobject.h \ $(srcdir)/Include/internal/pycore_getopt.h \ $(srcdir)/Include/internal/pycore_gil.h \ + $(srcdir)/Include/internal/pycore_global_dict_state.h \ $(srcdir)/Include/internal/pycore_global_objects.h \ $(srcdir)/Include/internal/pycore_global_objects_fini_generated.h \ $(srcdir)/Include/internal/pycore_hamt.h \ diff --git a/Objects/dictobject.c b/Objects/dictobject.c index d97bae5c067222..2d6cc95e4707e4 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -5897,10 +5897,10 @@ uint32_t _PyDictKeys_GetVersionForCurrentState(PyInterpreterState *interp, if (dictkeys->dk_version != 0) { return dictkeys->dk_version; } - if (interp->dict_state.next_keys_version == 0) { + if (interp->global_dict_state.next_keys_version == 0) { return 0; } - uint32_t v = interp->dict_state.next_keys_version++; + uint32_t v = interp->global_dict_state.next_keys_version++; dictkeys->dk_version = v; return v; } @@ -5912,7 +5912,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_state.watchers[watcher_id]) { + if (!interp->global_dict_state.watchers[watcher_id]) { PyErr_Format(PyExc_ValueError, "No dict watcher set for ID %d", watcher_id); return -1; } @@ -5955,8 +5955,8 @@ PyDict_AddWatcher(PyDict_WatchCallback callback) PyInterpreterState *interp = _PyInterpreterState_GET(); for (int i = 0; i < DICT_MAX_WATCHERS; i++) { - if (!interp->dict_state.watchers[i]) { - interp->dict_state.watchers[i] = callback; + if (!interp->global_dict_state.watchers[i]) { + interp->global_dict_state.watchers[i] = callback; return i; } } @@ -5972,7 +5972,7 @@ PyDict_ClearWatcher(int watcher_id) if (validate_watcher_id(interp, watcher_id)) { return -1; } - interp->dict_state.watchers[watcher_id] = NULL; + interp->global_dict_state.watchers[watcher_id] = NULL; return 0; } @@ -5998,7 +5998,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_state.watchers[i]; + PyDict_WatchCallback cb = interp->global_dict_state.watchers[i]; if (cb && (cb(event, (PyObject*)mp, key, value) < 0)) { // We don't want to resurrect the dict by potentially having an // unraisablehook keep a reference to it, so we don't pass the diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index 1860286fcd6195..8333a9c545a3b2 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -236,6 +236,7 @@ + diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters index 199e972cadeb51..59f1e87308f06d 100644 --- a/PCbuild/pythoncore.vcxproj.filters +++ b/PCbuild/pythoncore.vcxproj.filters @@ -636,6 +636,9 @@ Include\internal + + Include\internal + Include\internal diff --git a/Python/pystate.c b/Python/pystate.c index 3f1f63ce38a386..b7df7e61d7050f 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -855,7 +855,7 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate) } for (int i=0; i < DICT_MAX_WATCHERS; i++) { - interp->dict_state.watchers[i] = NULL; + interp->global_dict_state.watchers[i] = NULL; } for (int i=0; i < TYPE_MAX_WATCHERS; i++) { From b2d4606a254ed5b10c46329c4a6aed18d3720c16 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Sat, 20 Jan 2024 02:47:50 +0900 Subject: [PATCH 03/22] Update Objects/dictobject.c Co-authored-by: Sam Gross --- Objects/dictobject.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 2d6cc95e4707e4..e05c29dd0b6e7b 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -2467,7 +2467,9 @@ dict_dealloc(PyObject *self) } #ifdef WITH_FREELISTS struct _Py_dict_state *state = get_dict_state(); - if (state->numfree < PyDict_MAXFREELIST && state->numfree >=0 && Py_IS_TYPE(mp, &PyDict_Type)) { + if (state->numfree < PyDict_MAXFREELIST && state->numfree >=0 && + Py_IS_TYPE(mp, &PyDict_Type)) + { state->free_list[state->numfree++] = mp; OBJECT_STAT_INC(to_freelist); } From 8889072eda1c8e421b145b39359e4bce14c4d303 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Sat, 20 Jan 2024 02:59:55 +0900 Subject: [PATCH 04/22] nit --- Objects/dictobject.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objects/dictobject.c b/Objects/dictobject.c index e05c29dd0b6e7b..21bf9d8c40dae8 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -2467,7 +2467,7 @@ dict_dealloc(PyObject *self) } #ifdef WITH_FREELISTS struct _Py_dict_state *state = get_dict_state(); - if (state->numfree < PyDict_MAXFREELIST && state->numfree >=0 && + if (state->numfree < PyDict_MAXFREELIST && state->numfree >=0 && Py_IS_TYPE(mp, &PyDict_Type)) { state->free_list[state->numfree++] = mp; From 0cecf6661edebc38516bfc2497041774612da0e9 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Sat, 20 Jan 2024 03:04:49 +0900 Subject: [PATCH 05/22] Remove duplicated DICT_MAX_WATCHERS --- Include/internal/pycore_freelist.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/Include/internal/pycore_freelist.h b/Include/internal/pycore_freelist.h index 44b3f8a93fadb0..a6b5efbff94e05 100644 --- a/Include/internal/pycore_freelist.h +++ b/Include/internal/pycore_freelist.h @@ -65,8 +65,6 @@ struct _Py_float_state { #endif }; -#define DICT_MAX_WATCHERS 8 - struct _Py_dict_state { #ifdef WITH_FREELISTS /* Dictionary reuse scheme to save calls to malloc and free */ From 7041d4a74f14729dc952d61538fbd9581f781478 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Sat, 20 Jan 2024 03:06:06 +0900 Subject: [PATCH 06/22] nit --- Include/internal/pycore_global_dict_state.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Include/internal/pycore_global_dict_state.h b/Include/internal/pycore_global_dict_state.h index da4c9b0c7d9edc..c3ca987bcc4e1a 100644 --- a/Include/internal/pycore_global_dict_state.h +++ b/Include/internal/pycore_global_dict_state.h @@ -1,5 +1,5 @@ -#ifndef Py_INTERNAL_DICT_GLOBAL_STATE_H -#define Py_INTERNAL_DICT_GLOBAL_STATE_H +#ifndef Py_INTERNAL_GLOBAL_DICT_STATE_H +#define Py_INTERNAL_GLOBAL_DICT_STATE_H #ifdef __cplusplus extern "C" { #endif @@ -28,4 +28,4 @@ struct _Py_global_dict_state { #ifdef __cplusplus } #endif -#endif /* !Py_INTERNAL_DICT_GLOBAL_STATE_H */ \ No newline at end of file +#endif /* !Py_INTERNAL_GLOBAL_DICT_STATE_H */ From e973352ec84b4574b66875f3b7caa3f70e2d188c Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Sat, 20 Jan 2024 09:26:47 +0900 Subject: [PATCH 07/22] Address code review --- Include/internal/pycore_dict.h | 2 +- Include/internal/pycore_global_dict_state.h | 2 +- Include/internal/pycore_interp.h | 2 +- Include/internal/pycore_runtime_init.h | 2 +- Objects/dictobject.c | 14 +++++++------- Python/pystate.c | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Include/internal/pycore_dict.h b/Include/internal/pycore_dict.h index f2fad22bc872d2..3643b31ea46c49 100644 --- a/Include/internal/pycore_dict.h +++ b/Include/internal/pycore_dict.h @@ -211,7 +211,7 @@ static inline PyDictUnicodeEntry* DK_UNICODE_ENTRIES(PyDictKeysObject *dk) { #define DICT_VERSION_MASK (DICT_VERSION_INCREMENT - 1) #define DICT_NEXT_VERSION(INTERP) \ - ((INTERP)->global_dict_state.global_version += DICT_VERSION_INCREMENT) + ((INTERP)->dict_state.global_version += DICT_VERSION_INCREMENT) void _PyDict_SendEvent(int watcher_bits, diff --git a/Include/internal/pycore_global_dict_state.h b/Include/internal/pycore_global_dict_state.h index c3ca987bcc4e1a..c4ad3d8cbf08ee 100644 --- a/Include/internal/pycore_global_dict_state.h +++ b/Include/internal/pycore_global_dict_state.h @@ -10,7 +10,7 @@ extern "C" { #define DICT_MAX_WATCHERS 8 -struct _Py_global_dict_state { +struct _Py_dict_interp_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. */ diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h index 663d5055fb2ff4..7609ac4e9e824e 100644 --- a/Include/internal/pycore_interp.h +++ b/Include/internal/pycore_interp.h @@ -189,7 +189,7 @@ struct _is { struct _dtoa_state dtoa; struct _py_func_state func_state; - struct _Py_global_dict_state global_dict_state; + struct _Py_dict_interp_state dict_state; struct _Py_exc_state exc_state; struct ast_state ast; diff --git a/Include/internal/pycore_runtime_init.h b/Include/internal/pycore_runtime_init.h index e6de0cdde72dd2..5f47d60de37825 100644 --- a/Include/internal/pycore_runtime_init.h +++ b/Include/internal/pycore_runtime_init.h @@ -167,7 +167,7 @@ extern PyTypeObject _PyExc_MemoryError; }, \ .object_state = _py_object_state_INIT(INTERP), \ .dtoa = _dtoa_state_INIT(&(INTERP)), \ - .global_dict_state = _dict_state_INIT, \ + .dict_state = _dict_state_INIT, \ .func_state = { \ .next_version = 1, \ }, \ diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 21bf9d8c40dae8..f934d84b33c35c 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -5899,10 +5899,10 @@ uint32_t _PyDictKeys_GetVersionForCurrentState(PyInterpreterState *interp, if (dictkeys->dk_version != 0) { return dictkeys->dk_version; } - if (interp->global_dict_state.next_keys_version == 0) { + if (interp->dict_state.next_keys_version == 0) { return 0; } - uint32_t v = interp->global_dict_state.next_keys_version++; + uint32_t v = interp->dict_state.next_keys_version++; dictkeys->dk_version = v; return v; } @@ -5914,7 +5914,7 @@ validate_watcher_id(PyInterpreterState *interp, int watcher_id) PyErr_Format(PyExc_ValueError, "Invalid dict watcher ID %d", watcher_id); return -1; } - if (!interp->global_dict_state.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; } @@ -5957,8 +5957,8 @@ PyDict_AddWatcher(PyDict_WatchCallback callback) PyInterpreterState *interp = _PyInterpreterState_GET(); for (int i = 0; i < DICT_MAX_WATCHERS; i++) { - if (!interp->global_dict_state.watchers[i]) { - interp->global_dict_state.watchers[i] = callback; + if (!interp->dict_state.watchers[i]) { + interp->dict_state.watchers[i] = callback; return i; } } @@ -5974,7 +5974,7 @@ PyDict_ClearWatcher(int watcher_id) if (validate_watcher_id(interp, watcher_id)) { return -1; } - interp->global_dict_state.watchers[watcher_id] = NULL; + interp->dict_state.watchers[watcher_id] = NULL; return 0; } @@ -6000,7 +6000,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->global_dict_state.watchers[i]; + PyDict_WatchCallback cb = interp->dict_state.watchers[i]; if (cb && (cb(event, (PyObject*)mp, key, value) < 0)) { // We don't want to resurrect the dict by potentially having an // unraisablehook keep a reference to it, so we don't pass the diff --git a/Python/pystate.c b/Python/pystate.c index b7df7e61d7050f..3f1f63ce38a386 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -855,7 +855,7 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate) } for (int i=0; i < DICT_MAX_WATCHERS; i++) { - interp->global_dict_state.watchers[i] = NULL; + interp->dict_state.watchers[i] = NULL; } for (int i=0; i < TYPE_MAX_WATCHERS; i++) { From 514a66faa91ca73eec49e67af7a0fa45e48345ee Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Sat, 20 Jan 2024 09:34:22 +0900 Subject: [PATCH 08/22] Remove unnecessary changes --- Include/internal/pycore_global_dict_state.h | 31 --------- Include/internal/pycore_interp.h | 71 +++++++++++++-------- Makefile.pre.in | 1 - PCbuild/pythoncore.vcxproj | 1 - PCbuild/pythoncore.vcxproj.filters | 3 - 5 files changed, 43 insertions(+), 64 deletions(-) delete mode 100644 Include/internal/pycore_global_dict_state.h diff --git a/Include/internal/pycore_global_dict_state.h b/Include/internal/pycore_global_dict_state.h deleted file mode 100644 index c4ad3d8cbf08ee..00000000000000 --- a/Include/internal/pycore_global_dict_state.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef Py_INTERNAL_GLOBAL_DICT_STATE_H -#define Py_INTERNAL_GLOBAL_DICT_STATE_H -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef Py_BUILD_CORE -# error "this header requires Py_BUILD_CORE define" -#endif - -#define DICT_MAX_WATCHERS 8 - -struct _Py_dict_interp_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; - PyDict_WatchCallback watchers[DICT_MAX_WATCHERS]; -}; - -#define _dict_state_INIT \ - { \ - .next_keys_version = 2, \ - } - - -#ifdef __cplusplus -} -#endif -#endif /* !Py_INTERNAL_GLOBAL_DICT_STATE_H */ diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h index 7609ac4e9e824e..e6b8756763fec3 100644 --- a/Include/internal/pycore_interp.h +++ b/Include/internal/pycore_interp.h @@ -8,40 +8,55 @@ extern "C" { # error "this header requires Py_BUILD_CORE define" #endif -#include // bool - -#include "pycore_ast_state.h" // struct ast_state -#include "pycore_atexit.h" // struct atexit_state -#include "pycore_ceval_state.h" // struct _ceval_state -#include "pycore_code.h" // struct callable_cache -#include "pycore_context.h" // struct _Py_context_state -#include "pycore_crossinterp.h" // struct _xidregistry -#include "pycore_dtoa.h" // struct _dtoa_state -#include "pycore_exceptions.h" // struct _Py_exc_state -#include "pycore_floatobject.h" // struct _Py_float_state -#include "pycore_freelist.h" // struct _Py_freelist_state -#include "pycore_function.h" // FUNC_MAX_WATCHERS -#include "pycore_gc.h" // struct _gc_runtime_state -#include "pycore_genobject.h" // struct _Py_async_gen_state -#include "pycore_global_dict_state.h" // struct _Py_global_dict_state -#include "pycore_global_objects.h" // struct _Py_interp_cached_objects -#include "pycore_import.h" // struct _import_state -#include "pycore_instruments.h" // _PY_MONITORING_EVENTS -#include "pycore_list.h" // struct _Py_list_state -#include "pycore_mimalloc.h" // struct _mimalloc_interp_state -#include "pycore_object_state.h" // struct _py_object_state -#include "pycore_obmalloc.h" // struct _obmalloc_state -#include "pycore_tstate.h" // _PyThreadStateImpl -#include "pycore_tuple.h" // struct _Py_tuple_state -#include "pycore_typeobject.h" // struct types_state -#include "pycore_unicodeobject.h" // struct _Py_unicode_state -#include "pycore_warnings.h" // struct _warnings_runtime_state +#include // bool + +#include "pycore_ast_state.h" // struct ast_state +#include "pycore_atexit.h" // struct atexit_state +#include "pycore_ceval_state.h" // struct _ceval_state +#include "pycore_code.h" // struct callable_cache +#include "pycore_context.h" // struct _Py_context_state +#include "pycore_crossinterp.h" // struct _xidregistry +#include "pycore_dtoa.h" // struct _dtoa_state +#include "pycore_exceptions.h" // struct _Py_exc_state +#include "pycore_floatobject.h" // struct _Py_float_state +#include "pycore_freelist.h" // struct _Py_freelist_state +#include "pycore_function.h" // FUNC_MAX_WATCHERS +#include "pycore_gc.h" // struct _gc_runtime_state +#include "pycore_genobject.h" // struct _Py_async_gen_state +#include "pycore_global_objects.h"// struct _Py_interp_cached_objects +#include "pycore_import.h" // struct _import_state +#include "pycore_instruments.h" // _PY_MONITORING_EVENTS +#include "pycore_list.h" // struct _Py_list_state +#include "pycore_mimalloc.h" // struct _mimalloc_interp_state +#include "pycore_object_state.h" // struct _py_object_state +#include "pycore_obmalloc.h" // struct _obmalloc_state +#include "pycore_tstate.h" // _PyThreadStateImpl +#include "pycore_tuple.h" // struct _Py_tuple_state +#include "pycore_typeobject.h" // struct types_state +#include "pycore_unicodeobject.h" // struct _Py_unicode_state +#include "pycore_warnings.h" // struct _warnings_runtime_state struct _Py_long_state { int max_str_digits; }; +#define DICT_MAX_WATCHERS 8 + +struct _Py_dict_interp_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; + PyDict_WatchCallback watchers[DICT_MAX_WATCHERS]; +}; + +#define _dict_state_INIT \ + { \ + .next_keys_version = 2, \ + } + /* cross-interpreter data registry */ diff --git a/Makefile.pre.in b/Makefile.pre.in index 9168fea98b6a11..e5a2639a5f5c37 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1834,7 +1834,6 @@ PYTHON_HEADERS= \ $(srcdir)/Include/internal/pycore_genobject.h \ $(srcdir)/Include/internal/pycore_getopt.h \ $(srcdir)/Include/internal/pycore_gil.h \ - $(srcdir)/Include/internal/pycore_global_dict_state.h \ $(srcdir)/Include/internal/pycore_global_objects.h \ $(srcdir)/Include/internal/pycore_global_objects_fini_generated.h \ $(srcdir)/Include/internal/pycore_hamt.h \ diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index 8333a9c545a3b2..1860286fcd6195 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -236,7 +236,6 @@ - diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters index 59f1e87308f06d..199e972cadeb51 100644 --- a/PCbuild/pythoncore.vcxproj.filters +++ b/PCbuild/pythoncore.vcxproj.filters @@ -636,9 +636,6 @@ Include\internal - - Include\internal - Include\internal From ef2b754a2ae2f7990c2b5377e946c823d221d7ae Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Fri, 26 Jan 2024 11:31:17 +0900 Subject: [PATCH 09/22] Address code review --- Include/internal/pycore_dict_state.h | 31 ++++++++++++++++++++++++++++ Include/internal/pycore_interp.h | 17 +-------------- Makefile.pre.in | 1 + PCbuild/pythoncore.vcxproj | 1 + PCbuild/pythoncore.vcxproj.filters | 3 +++ 5 files changed, 37 insertions(+), 16 deletions(-) create mode 100644 Include/internal/pycore_dict_state.h diff --git a/Include/internal/pycore_dict_state.h b/Include/internal/pycore_dict_state.h new file mode 100644 index 00000000000000..27bfad504023f5 --- /dev/null +++ b/Include/internal/pycore_dict_state.h @@ -0,0 +1,31 @@ +#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 + +#define DICT_MAX_WATCHERS 8 + +struct _Py_dict_interp_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; + PyDict_WatchCallback watchers[DICT_MAX_WATCHERS]; +}; + +#define _dict_state_INIT \ + { \ + .next_keys_version = 2, \ + } + + +#ifdef __cplusplus +} +#endif +#endif /* !Py_INTERNAL_DICT_STATE_H */ diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h index 407fc0dbf9ae4f..6247119f57d49d 100644 --- a/Include/internal/pycore_interp.h +++ b/Include/internal/pycore_interp.h @@ -16,6 +16,7 @@ extern "C" { #include "pycore_code.h" // struct callable_cache #include "pycore_context.h" // struct _Py_context_state #include "pycore_crossinterp.h" // struct _xidregistry +#include "pycore_dict_state.h" // struct _Py_dict_interp_state #include "pycore_dtoa.h" // struct _dtoa_state #include "pycore_exceptions.h" // struct _Py_exc_state #include "pycore_floatobject.h" // struct _Py_float_state @@ -41,22 +42,6 @@ struct _Py_long_state { int max_str_digits; }; -#define DICT_MAX_WATCHERS 8 - -struct _Py_dict_interp_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; - PyDict_WatchCallback watchers[DICT_MAX_WATCHERS]; -}; - -#define _dict_state_INIT \ - { \ - .next_keys_version = 2, \ - } - // Support for stop-the-world events. This exists in both the PyRuntime struct // for global pauses and in each PyInterpreterState for per-interpreter pauses. struct _stoptheworld_state { diff --git a/Makefile.pre.in b/Makefile.pre.in index 53a38d954a7323..21b122ae0fcd9f 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1821,6 +1821,7 @@ PYTHON_HEADERS= \ $(srcdir)/Include/internal/pycore_critical_section.h \ $(srcdir)/Include/internal/pycore_crossinterp.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/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index 39a8bfcf8402d1..a121bd231f53f3 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -222,6 +222,7 @@ + diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters index 3736401cfc6a46..0dbb80aaaed36d 100644 --- a/PCbuild/pythoncore.vcxproj.filters +++ b/PCbuild/pythoncore.vcxproj.filters @@ -594,6 +594,9 @@ Include\internal + + Include\internal + Include\internal From e038141c70b354190820823c2cd86b9d12dd2bf0 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Fri, 26 Jan 2024 11:48:02 +0900 Subject: [PATCH 10/22] Fix slash --- PCbuild/pythoncore.vcxproj | 2 +- PCbuild/pythoncore.vcxproj.filters | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index a121bd231f53f3..57275fb2039ee0 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -222,7 +222,7 @@ - + diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters index 0dbb80aaaed36d..51cbb079b5b550 100644 --- a/PCbuild/pythoncore.vcxproj.filters +++ b/PCbuild/pythoncore.vcxproj.filters @@ -594,7 +594,7 @@ Include\internal - + Include\internal From e808bab010e125cd35cc12d98a7d808ff3118dbd Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Fri, 26 Jan 2024 16:10:17 +0900 Subject: [PATCH 11/22] Address code review --- Include/internal/pycore_freelist.h | 2 +- Objects/dictobject.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Include/internal/pycore_freelist.h b/Include/internal/pycore_freelist.h index a6b5efbff94e05..d82b0e8eeb5c68 100644 --- a/Include/internal/pycore_freelist.h +++ b/Include/internal/pycore_freelist.h @@ -109,7 +109,7 @@ typedef struct _Py_freelist_state { struct _Py_float_state float_state; struct _Py_tuple_state tuple_state; struct _Py_list_state list_state; - struct _Py_dict_state dict_state; + struct _Py_dict_state dicts; struct _Py_slice_state slice_state; struct _Py_context_state context_state; struct _Py_async_gen_state async_gen_state; diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 7b720279492d82..8f5cd9b35bbbf8 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -248,7 +248,7 @@ static struct _Py_dict_state * get_dict_state(void) { _PyFreeListState *state = _PyFreeListState_GET(); - return &state->dict_state; + return &state->dicts; } #endif @@ -257,7 +257,7 @@ void _PyDict_ClearFreeList(_PyFreeListState *freelist_state, int is_finalization) { #ifdef WITH_FREELISTS - struct _Py_dict_state *state = &freelist_state->dict_state; + struct _Py_dict_state *state = &freelist_state->dicts; while (state->numfree > 0) { PyDictObject *op = state->free_list[--state->numfree]; assert(PyDict_CheckExact(op)); From e91d8ffafbc986d2b7288620478c46c3302d0b85 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Sat, 27 Jan 2024 00:57:29 +0900 Subject: [PATCH 12/22] Address code review --- Objects/dictobject.c | 3 +++ Objects/floatobject.c | 3 +++ Objects/genobject.c | 3 +++ Objects/listobject.c | 3 +++ Python/context.c | 3 +++ Python/pystate.c | 4 ++++ 6 files changed, 19 insertions(+) diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 8f5cd9b35bbbf8..00f947bdfdef5c 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -276,6 +276,9 @@ _PyDict_ClearFreeList(_PyFreeListState *freelist_state, int is_finalization) void _PyDict_Fini(_PyFreeListState *freelist_state) { + // With Py_GIL_DISABLED: + // See `_Py_ClearFreeLists()` about why we only need to clear the freelists + // for the current thread state. _PyDict_ClearFreeList(freelist_state, 1); } diff --git a/Objects/floatobject.c b/Objects/floatobject.c index b7611d5f96ac3b..ca95bb6fb6abaa 100644 --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -2013,6 +2013,9 @@ _PyFloat_ClearFreeList(_PyFreeListState *freelist_state, int is_finalization) void _PyFloat_Fini(_PyFreeListState *state) { + // With Py_GIL_DISABLED: + // See `_Py_ClearFreeLists()` about why we only need to clear the freelists + // for the current thread state. _PyFloat_ClearFreeList(state, 1); } diff --git a/Objects/genobject.c b/Objects/genobject.c index f47197330fdd80..94ad27a6984057 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -1685,6 +1685,9 @@ _PyAsyncGen_ClearFreeLists(_PyFreeListState *freelist_state, int is_finalization void _PyAsyncGen_Fini(_PyFreeListState *state) { + // With Py_GIL_DISABLED: + // See `_Py_ClearFreeLists()` about why we only need to clear the freelists + // for the current thread state. _PyAsyncGen_ClearFreeLists(state, 1); } diff --git a/Objects/listobject.c b/Objects/listobject.c index 1e885f9cb80c4c..e5fb1618e71260 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -139,6 +139,9 @@ _PyList_ClearFreeList(_PyFreeListState *freelist_state, int is_finalization) void _PyList_Fini(_PyFreeListState *state) { + // With Py_GIL_DISABLED: + // See `_Py_ClearFreeLists()` about why we only need to clear the freelists + // for the current thread state. _PyList_ClearFreeList(state, 1); } diff --git a/Python/context.c b/Python/context.c index 294485e5b407df..b4a9292808731b 100644 --- a/Python/context.c +++ b/Python/context.c @@ -1287,6 +1287,9 @@ _PyContext_ClearFreeList(_PyFreeListState *freelist_state, int is_finalization) void _PyContext_Fini(_PyFreeListState *state) { + // With Py_GIL_DISABLED: + // See `_Py_ClearFreeLists()` about why we only need to clear the freelists + // for the current thread state. _PyContext_ClearFreeList(state, 1); } diff --git a/Python/pystate.c b/Python/pystate.c index d1113ace86d86b..54252731293833 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -1464,6 +1464,10 @@ clear_datastack(PyThreadState *tstate) void _Py_ClearFreeLists(_PyFreeListState *state, int is_finalization) { + // With Py_GIL_DISABLED: + // We only need to clear the freelists for the thread state we're using + // to finalize the current interpreter. Freelists for all other thread states + // are cleared elsewhere. _PyFloat_ClearFreeList(state, is_finalization); _PyTuple_ClearFreeList(state, is_finalization); _PyList_ClearFreeList(state, is_finalization); From b9056923213c296d6413792b83476439b54b916c Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Sat, 27 Jan 2024 01:05:24 +0900 Subject: [PATCH 13/22] Address code review --- Include/internal/pycore_dict_state.h | 2 +- Include/internal/pycore_freelist.h | 4 ++-- Include/internal/pycore_interp.h | 4 ++-- Objects/dictobject.c | 16 ++++++++-------- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Include/internal/pycore_dict_state.h b/Include/internal/pycore_dict_state.h index 27bfad504023f5..a6dd63d36e040e 100644 --- a/Include/internal/pycore_dict_state.h +++ b/Include/internal/pycore_dict_state.h @@ -10,7 +10,7 @@ extern "C" { #define DICT_MAX_WATCHERS 8 -struct _Py_dict_interp_state { +struct _Py_dict_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. */ diff --git a/Include/internal/pycore_freelist.h b/Include/internal/pycore_freelist.h index d0b684d89d2797..82a42300991ecc 100644 --- a/Include/internal/pycore_freelist.h +++ b/Include/internal/pycore_freelist.h @@ -67,7 +67,7 @@ struct _Py_float_state { #endif }; -struct _Py_dict_state { +struct _Py_dict_freelist { #ifdef WITH_FREELISTS /* Dictionary reuse scheme to save calls to malloc and free */ PyDictObject *free_list[PyDict_MAXFREELIST]; @@ -118,7 +118,7 @@ typedef struct _Py_freelist_state { struct _Py_float_state floats; struct _Py_tuple_state tuples; struct _Py_list_state lists; - struct _Py_dict_state dicts; + struct _Py_dict_freelist dicts; struct _Py_slice_state slices; struct _Py_context_state contexts; struct _Py_async_gen_state async_gens; diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h index fe0cc9539577ae..f2c2fd26d91d72 100644 --- a/Include/internal/pycore_interp.h +++ b/Include/internal/pycore_interp.h @@ -16,7 +16,7 @@ extern "C" { #include "pycore_code.h" // struct callable_cache #include "pycore_context.h" // struct _Py_context_state #include "pycore_crossinterp.h" // struct _xidregistry -#include "pycore_dict_state.h" // struct _Py_dict_interp_state +#include "pycore_dict_state.h" // struct _Py_dict_state #include "pycore_dtoa.h" // struct _dtoa_state #include "pycore_exceptions.h" // struct _Py_exc_state #include "pycore_floatobject.h" // struct _Py_float_state @@ -221,7 +221,7 @@ struct _is { struct _dtoa_state dtoa; struct _py_func_state func_state; - struct _Py_dict_interp_state dict_state; + struct _Py_dict_state dict_state; struct _Py_exc_state exc_state; struct ast_state ast; diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 00f947bdfdef5c..e8b568dc8bf9ea 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -244,7 +244,7 @@ static PyObject* dict_iter(PyObject *dict); #ifdef WITH_FREELISTS -static struct _Py_dict_state * +static struct _Py_dict_freelist * get_dict_state(void) { _PyFreeListState *state = _PyFreeListState_GET(); @@ -257,7 +257,7 @@ void _PyDict_ClearFreeList(_PyFreeListState *freelist_state, int is_finalization) { #ifdef WITH_FREELISTS - struct _Py_dict_state *state = &freelist_state->dicts; + struct _Py_dict_freelist *state = &freelist_state->dicts; while (state->numfree > 0) { PyDictObject *op = state->free_list[--state->numfree]; assert(PyDict_CheckExact(op)); @@ -294,7 +294,7 @@ void _PyDict_DebugMallocStats(FILE *out) { #ifdef WITH_FREELISTS - struct _Py_dict_state *state = get_dict_state(); + struct _Py_dict_freelist *state = get_dict_state(); _PyDebugAllocatorStats(out, "free PyDictObject", state->numfree, sizeof(PyDictObject)); #endif @@ -630,7 +630,7 @@ new_keys_object(PyInterpreterState *interp, uint8_t log2_size, bool unicode) } #ifdef WITH_FREELISTS - struct _Py_dict_state *state = get_dict_state(); + struct _Py_dict_freelist *state = get_dict_state(); if (log2_size == PyDict_LOG_MINSIZE && unicode && state->keys_numfree > 0) { dk = state->keys_free_list[--state->keys_numfree]; OBJECT_STAT_INC(from_freelist); @@ -682,7 +682,7 @@ free_keys_object(PyInterpreterState *interp, PyDictKeysObject *keys) } } #ifdef WITH_FREELISTS - struct _Py_dict_state *state = get_dict_state(); + struct _Py_dict_freelist *state = get_dict_state(); if (DK_LOG_SIZE(keys) == PyDict_LOG_MINSIZE && state->keys_numfree < PyDict_MAXFREELIST && state->keys_numfree >= 0 @@ -727,7 +727,7 @@ new_dict(PyInterpreterState *interp, PyDictObject *mp; assert(keys != NULL); #ifdef WITH_FREELISTS - struct _Py_dict_state *state = get_dict_state(); + struct _Py_dict_freelist *state = get_dict_state(); if (state->numfree > 0) { mp = state->free_list[--state->numfree]; assert (mp != NULL); @@ -1544,7 +1544,7 @@ dictresize(PyInterpreterState *interp, PyDictObject *mp, assert(oldkeys->dk_kind != DICT_KEYS_SPLIT); assert(oldkeys->dk_refcnt == 1); #ifdef WITH_FREELISTS - struct _Py_dict_state *state = get_dict_state(); + struct _Py_dict_freelist *state = get_dict_state(); if (DK_LOG_SIZE(oldkeys) == PyDict_LOG_MINSIZE && DK_IS_UNICODE(oldkeys) && state->keys_numfree < PyDict_MAXFREELIST && @@ -2469,7 +2469,7 @@ dict_dealloc(PyObject *self) dictkeys_decref(interp, keys); } #ifdef WITH_FREELISTS - struct _Py_dict_state *state = get_dict_state(); + struct _Py_dict_freelist *state = get_dict_state(); if (state->numfree < PyDict_MAXFREELIST && state->numfree >=0 && Py_IS_TYPE(mp, &PyDict_Type)) { From 4f7d9217d6f7c9d8197a9dbb55b2604a81bba7da Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Sat, 27 Jan 2024 01:12:29 +0900 Subject: [PATCH 14/22] Address code review --- Objects/dictobject.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Objects/dictobject.c b/Objects/dictobject.c index e8b568dc8bf9ea..acf8d4c0baba24 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -279,7 +279,9 @@ _PyDict_Fini(_PyFreeListState *freelist_state) // With Py_GIL_DISABLED: // See `_Py_ClearFreeLists()` about why we only need to clear the freelists // for the current thread state. +#ifndef Py_GIL_DISABLED _PyDict_ClearFreeList(freelist_state, 1); +#endif } static inline Py_hash_t From 9239cdc78b7ce05b09af8e1f8e0ac35f0e5b766c Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Sat, 27 Jan 2024 01:18:13 +0900 Subject: [PATCH 15/22] Address code review --- Objects/floatobject.c | 2 ++ Objects/genobject.c | 2 ++ Objects/listobject.c | 2 ++ Python/context.c | 2 ++ 4 files changed, 8 insertions(+) diff --git a/Objects/floatobject.c b/Objects/floatobject.c index ca95bb6fb6abaa..fadb72ffd382d3 100644 --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -2016,7 +2016,9 @@ _PyFloat_Fini(_PyFreeListState *state) // With Py_GIL_DISABLED: // See `_Py_ClearFreeLists()` about why we only need to clear the freelists // for the current thread state. +#ifndef Py_GIL_DISABLED _PyFloat_ClearFreeList(state, 1); +#endif } void diff --git a/Objects/genobject.c b/Objects/genobject.c index 94ad27a6984057..c99df3c197f49e 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -1688,7 +1688,9 @@ _PyAsyncGen_Fini(_PyFreeListState *state) // With Py_GIL_DISABLED: // See `_Py_ClearFreeLists()` about why we only need to clear the freelists // for the current thread state. +#ifndef Py_GIL_DISABLED _PyAsyncGen_ClearFreeLists(state, 1); +#endif } diff --git a/Objects/listobject.c b/Objects/listobject.c index e5fb1618e71260..295abf8a1f634d 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -142,7 +142,9 @@ _PyList_Fini(_PyFreeListState *state) // With Py_GIL_DISABLED: // See `_Py_ClearFreeLists()` about why we only need to clear the freelists // for the current thread state. +#ifndef Py_GIL_DISABLED _PyList_ClearFreeList(state, 1); +#endif } /* Print summary info about the state of the optimized allocator */ diff --git a/Python/context.c b/Python/context.c index b4a9292808731b..b87fb9bb954ff8 100644 --- a/Python/context.c +++ b/Python/context.c @@ -1290,7 +1290,9 @@ _PyContext_Fini(_PyFreeListState *state) // With Py_GIL_DISABLED: // See `_Py_ClearFreeLists()` about why we only need to clear the freelists // for the current thread state. +#ifndef Py_GIL_DISABLED _PyContext_ClearFreeList(state, 1); +#endif } From c12b1c1dcb1ac82b012cdba76da1b3f0b9517206 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Sat, 27 Jan 2024 01:40:02 +0900 Subject: [PATCH 16/22] Improve comments --- Objects/dictobject.c | 3 +-- Objects/genobject.c | 3 +-- Objects/listobject.c | 3 +-- Python/context.c | 3 +-- Python/pystate.c | 6 ++---- 5 files changed, 6 insertions(+), 12 deletions(-) diff --git a/Objects/dictobject.c b/Objects/dictobject.c index acf8d4c0baba24..b2f4ecdae59217 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -277,8 +277,7 @@ void _PyDict_Fini(_PyFreeListState *freelist_state) { // With Py_GIL_DISABLED: - // See `_Py_ClearFreeLists()` about why we only need to clear the freelists - // for the current thread state. + // the freelists for the current thread state have already been cleared. #ifndef Py_GIL_DISABLED _PyDict_ClearFreeList(freelist_state, 1); #endif diff --git a/Objects/genobject.c b/Objects/genobject.c index c99df3c197f49e..ab523e46cceaa3 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -1686,8 +1686,7 @@ void _PyAsyncGen_Fini(_PyFreeListState *state) { // With Py_GIL_DISABLED: - // See `_Py_ClearFreeLists()` about why we only need to clear the freelists - // for the current thread state. + // the freelists for the current thread state have already been cleared. #ifndef Py_GIL_DISABLED _PyAsyncGen_ClearFreeLists(state, 1); #endif diff --git a/Objects/listobject.c b/Objects/listobject.c index 295abf8a1f634d..8a739353901c1b 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -140,8 +140,7 @@ void _PyList_Fini(_PyFreeListState *state) { // With Py_GIL_DISABLED: - // See `_Py_ClearFreeLists()` about why we only need to clear the freelists - // for the current thread state. + // the freelists for the current thread state have already been cleared. #ifndef Py_GIL_DISABLED _PyList_ClearFreeList(state, 1); #endif diff --git a/Python/context.c b/Python/context.c index b87fb9bb954ff8..793dfa2b72c7e3 100644 --- a/Python/context.c +++ b/Python/context.c @@ -1288,8 +1288,7 @@ void _PyContext_Fini(_PyFreeListState *state) { // With Py_GIL_DISABLED: - // See `_Py_ClearFreeLists()` about why we only need to clear the freelists - // for the current thread state. + // the freelists for the current thread state have already been cleared. #ifndef Py_GIL_DISABLED _PyContext_ClearFreeList(state, 1); #endif diff --git a/Python/pystate.c b/Python/pystate.c index 54252731293833..e426685b69bd07 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -1464,10 +1464,8 @@ clear_datastack(PyThreadState *tstate) void _Py_ClearFreeLists(_PyFreeListState *state, int is_finalization) { - // With Py_GIL_DISABLED: - // We only need to clear the freelists for the thread state we're using - // to finalize the current interpreter. Freelists for all other thread states - // are cleared elsewhere. + // In the free-threaded build, freelists are per-PyThreadState and cleared in PyThreadState_Clear() + // In the default build, freelists are per-interpreter and cleared in finalize_interp_types() _PyFloat_ClearFreeList(state, is_finalization); _PyTuple_ClearFreeList(state, is_finalization); _PyList_ClearFreeList(state, is_finalization); From ccd3b24c2dbb0b35606b1591a90c0ed6c7372f4e Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Sat, 27 Jan 2024 07:13:07 +0900 Subject: [PATCH 17/22] Address code review --- Objects/floatobject.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Objects/floatobject.c b/Objects/floatobject.c index fadb72ffd382d3..c440e0dab0e79f 100644 --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -2014,8 +2014,7 @@ void _PyFloat_Fini(_PyFreeListState *state) { // With Py_GIL_DISABLED: - // See `_Py_ClearFreeLists()` about why we only need to clear the freelists - // for the current thread state. + // the freelists for the current thread state have already been cleared. #ifndef Py_GIL_DISABLED _PyFloat_ClearFreeList(state, 1); #endif From e1d9549a30ae551d01c4a8e8747f5d388cabff03 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Tue, 30 Jan 2024 09:30:00 +0900 Subject: [PATCH 18/22] nit --- Objects/dictobject.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Objects/dictobject.c b/Objects/dictobject.c index a99d06673fc9b8..772c46416a69ac 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -2455,8 +2455,7 @@ dict_dealloc(PyObject *self) #ifdef WITH_FREELISTS struct _Py_dict_freelist *state = get_dict_state(); if (state->numfree < PyDict_MAXFREELIST && state->numfree >=0 && - Py_IS_TYPE(mp, &PyDict_Type)) - { + Py_IS_TYPE(mp, &PyDict_Type)) { state->free_list[state->numfree++] = mp; OBJECT_STAT_INC(to_freelist); } From 4a76399c7c259bfe6a85927e20108151435463fe Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Fri, 2 Feb 2024 04:57:18 +0900 Subject: [PATCH 19/22] Revert _PyDict_Fini --- Include/internal/pycore_dict.h | 2 +- Objects/dictobject.c | 2 +- Python/pylifecycle.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Include/internal/pycore_dict.h b/Include/internal/pycore_dict.h index bcd516b0345709..60acd89cf6c34a 100644 --- a/Include/internal/pycore_dict.h +++ b/Include/internal/pycore_dict.h @@ -70,7 +70,7 @@ extern PyObject* _PyDictView_Intersect(PyObject* self, PyObject *other); /* runtime lifecycle */ -extern void _PyDict_Fini(_PyFreeListState *state); +extern void _PyDict_Fini(PyInterpreterState *state); /* other API */ diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 772c46416a69ac..3f882fdce0f72d 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -274,7 +274,7 @@ _PyDict_ClearFreeList(_PyFreeListState *freelist_state, int is_finalization) } void -_PyDict_Fini(_PyFreeListState *freelist_state) +_PyDict_Fini(PyInterpreterState *interp) { // With Py_GIL_DISABLED: // the freelists for the current thread state have already been cleared. diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 5b983d64284c00..372f60602375b6 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -1782,12 +1782,12 @@ finalize_interp_types(PyInterpreterState *interp) // a dict internally. _PyUnicode_ClearInterned(interp); + _PyDict_Fini(interp); _PyUnicode_Fini(interp); _PyFreeListState *state = _PyFreeListState_GET(); _PyTuple_Fini(state); _PyList_Fini(state); - _PyDict_Fini(state); _PyFloat_Fini(state); _PySlice_Fini(state); _PyContext_Fini(state); From 11f81d17ccede0be02f05fc38c45b8263cf03b92 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Fri, 2 Feb 2024 05:02:14 +0900 Subject: [PATCH 20/22] fix --- Objects/dictobject.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 3f882fdce0f72d..9b787f3d2f46a3 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -279,7 +279,7 @@ _PyDict_Fini(PyInterpreterState *interp) // With Py_GIL_DISABLED: // the freelists for the current thread state have already been cleared. #ifndef Py_GIL_DISABLED - _PyDict_ClearFreeList(freelist_state, 1); + _PyDict_ClearFreeList(&interp->freelist_state, 1); #endif } From 86e3228cd23c84eceb4dde292ae967c7e438ca34 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Fri, 2 Feb 2024 05:07:01 +0900 Subject: [PATCH 21/22] Address code review --- Objects/dictobject.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 9b787f3d2f46a3..e6219cb2deeed0 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -274,12 +274,13 @@ _PyDict_ClearFreeList(_PyFreeListState *freelist_state, int is_finalization) } void -_PyDict_Fini(PyInterpreterState *interp) +_PyDict_Fini(PyInterpreterState *Py_UNUSED(interp)) { // With Py_GIL_DISABLED: // the freelists for the current thread state have already been cleared. #ifndef Py_GIL_DISABLED - _PyDict_ClearFreeList(&interp->freelist_state, 1); + struct _Py_dict_freelist *freelist_state = get_dict_state(); + _PyDict_ClearFreeList(freelist_state, 1); #endif } From 42a3541d3791c1055e27488b6fe741b0e83663e7 Mon Sep 17 00:00:00 2001 From: Donghee Na Date: Fri, 2 Feb 2024 05:31:26 +0900 Subject: [PATCH 22/22] fix --- Objects/dictobject.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Objects/dictobject.c b/Objects/dictobject.c index e6219cb2deeed0..65db626d26c3e9 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -279,8 +279,8 @@ _PyDict_Fini(PyInterpreterState *Py_UNUSED(interp)) // With Py_GIL_DISABLED: // the freelists for the current thread state have already been cleared. #ifndef Py_GIL_DISABLED - struct _Py_dict_freelist *freelist_state = get_dict_state(); - _PyDict_ClearFreeList(freelist_state, 1); + _PyFreeListState *state = _PyFreeListState_GET(); + _PyDict_ClearFreeList(state, 1); #endif }