From 2a4c17e8964bca2c6c64df8314e525d9eedb3d1e Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Fri, 14 Apr 2023 12:22:10 -0700 Subject: [PATCH] pystate: move freelists to per-thread state --- Include/cpython/pystate.h | 6 ---- Include/internal/pycore_context.h | 2 +- Include/internal/pycore_dict.h | 7 +++-- Include/internal/pycore_dict_state.h | 6 +++- Include/internal/pycore_floatobject.h | 2 +- Include/internal/pycore_gc.h | 12 ++++---- Include/internal/pycore_genobject.h | 2 +- Include/internal/pycore_interp.h | 12 +++++--- Include/internal/pycore_list.h | 2 +- Include/internal/pycore_pystate.h | 5 +++ Include/internal/pycore_tuple.h | 2 +- Modules/gcmodule.c | 44 +++++++++++++++++++-------- Objects/dictobject.c | 31 ++++++++++--------- Objects/floatobject.c | 13 ++++---- Objects/genobject.c | 15 +++++---- Objects/listobject.c | 13 ++++---- Objects/tupleobject.c | 21 +++++++------ Python/context.c | 13 ++++---- Python/pylifecycle.c | 17 ++++++----- Python/pystate.c | 10 ++++++ 20 files changed, 135 insertions(+), 100 deletions(-) diff --git a/Include/cpython/pystate.h b/Include/cpython/pystate.h index 5c10b2af9ff..c342bf25ec2 100644 --- a/Include/cpython/pystate.h +++ b/Include/cpython/pystate.h @@ -106,10 +106,6 @@ typedef struct _stack_chunk { PyObject * data[1]; /* Variable sized */ } _PyStackChunk; -typedef struct _Py_dict_thread_state { - uint64_t dict_version; -} _Py_dict_thread_state; - struct mi_heap_s; typedef struct mi_heap_s mi_heap_t; typedef struct _PyEventRc _PyEventRc; @@ -156,8 +152,6 @@ struct _ts { * or most recently, executing _PyEval_EvalFrameDefault. */ _PyCFrame *cframe; - _Py_dict_thread_state dict_state; - /* The thread will not stop for GC or other stop-the-world requests. * Used for *short* critical sections that to prevent deadlocks between * finalizers and stopped threads. */ diff --git a/Include/internal/pycore_context.h b/Include/internal/pycore_context.h index 52dfe3ef233..e27dd8cb302 100644 --- a/Include/internal/pycore_context.h +++ b/Include/internal/pycore_context.h @@ -13,7 +13,7 @@ extern PyTypeObject _PyContextTokenMissing_Type; /* runtime lifecycle */ PyStatus _PyContext_Init(PyInterpreterState *); -void _PyContext_Fini(PyInterpreterState *); +void _PyContext_Fini(PyThreadState *); /* other API */ diff --git a/Include/internal/pycore_dict.h b/Include/internal/pycore_dict.h index 84575fb96a4..46e7d1129d5 100644 --- a/Include/internal/pycore_dict.h +++ b/Include/internal/pycore_dict.h @@ -16,7 +16,7 @@ extern "C" { /* runtime lifecycle */ -extern void _PyDict_Fini(PyInterpreterState *interp); +extern void _PyDict_Fini(PyThreadState *tstate); /* other API */ @@ -166,14 +166,15 @@ static inline PyDictSharedKeysObject* DK_AS_SPLIT(PyDictKeysObject *dk) { static inline uint64_t _PyDict_NextVersion(PyThreadState *tstate) { - uint64_t version = tstate->dict_state.dict_version; + struct _Py_dict_thread_state *dict_state = &((PyThreadStateImpl *)tstate)->dict_state; + uint64_t version = dict_state->dict_version; if (version % DICT_GLOBAL_VERSION_INCREMENT == 0) { version = _Py_atomic_add_uint64( &_PyRuntime.dict_state.global_version, DICT_GLOBAL_VERSION_INCREMENT); } version += DICT_VERSION_INCREMENT; - tstate->dict_state.dict_version = version; + dict_state->dict_version = version; return version; } diff --git a/Include/internal/pycore_dict_state.h b/Include/internal/pycore_dict_state.h index 3f3748ff3fe..8e6f236e34c 100644 --- a/Include/internal/pycore_dict_state.h +++ b/Include/internal/pycore_dict_state.h @@ -31,7 +31,8 @@ struct _Py_dict_runtime_state { typedef struct PyDictSharedKeysObject PyDictSharedKeysObject; -struct _Py_dict_state { +struct _Py_dict_thread_state { + uint64_t dict_version; #if PyDict_MAXFREELIST > 0 /* Dictionary reuse scheme to save calls to malloc and free */ PyDictObject *free_list[PyDict_MAXFREELIST]; @@ -39,6 +40,9 @@ struct _Py_dict_state { int numfree; int keys_numfree; #endif +}; + +struct _Py_dict_state { PyDict_WatchCallback watchers[DICT_MAX_WATCHERS]; /* shared keys from deallocated types (i.e., potentially dead) */ PyDictSharedKeysObject *tracked_shared_keys; diff --git a/Include/internal/pycore_floatobject.h b/Include/internal/pycore_floatobject.h index 27c63bc87f3..e2741d1aa1b 100644 --- a/Include/internal/pycore_floatobject.h +++ b/Include/internal/pycore_floatobject.h @@ -13,7 +13,7 @@ extern "C" { extern void _PyFloat_InitState(PyInterpreterState *); extern PyStatus _PyFloat_InitTypes(PyInterpreterState *); -extern void _PyFloat_Fini(PyInterpreterState *); +extern void _PyFloat_Fini(PyThreadState *); extern void _PyFloat_FiniType(PyInterpreterState *); diff --git a/Include/internal/pycore_gc.h b/Include/internal/pycore_gc.h index 30dcb92f5c8..5a9c13e3422 100644 --- a/Include/internal/pycore_gc.h +++ b/Include/internal/pycore_gc.h @@ -222,12 +222,12 @@ _PyGC_ShouldCollect(struct _gc_runtime_state *gcstate) } // Functions to clear types free lists -extern void _PyTuple_ClearFreeList(PyInterpreterState *interp); -extern void _PyFloat_ClearFreeList(PyInterpreterState *interp); -extern void _PyList_ClearFreeList(PyInterpreterState *interp); -extern void _PyDict_ClearFreeList(PyInterpreterState *interp); -extern void _PyAsyncGen_ClearFreeLists(PyInterpreterState *interp); -extern void _PyContext_ClearFreeList(PyInterpreterState *interp); +extern void _PyTuple_ClearFreeList(PyThreadState *tstate); +extern void _PyFloat_ClearFreeList(PyThreadState *tstate); +extern void _PyList_ClearFreeList(PyThreadState *tstate); +extern void _PyDict_ClearFreeList(PyThreadState *tstate); +extern void _PyAsyncGen_ClearFreeLists(PyThreadState *tstate); +extern void _PyContext_ClearFreeList(PyThreadState *tstate); extern void _Py_RunGC(PyThreadState *tstate); #ifdef __cplusplus diff --git a/Include/internal/pycore_genobject.h b/Include/internal/pycore_genobject.h index dc60b4ca705..8924e3e4221 100644 --- a/Include/internal/pycore_genobject.h +++ b/Include/internal/pycore_genobject.h @@ -14,7 +14,7 @@ extern PyObject *_PyAsyncGenValueWrapperNew(PyThreadState *state, PyObject *); /* runtime lifecycle */ -extern void _PyAsyncGen_Fini(PyInterpreterState *); +extern void _PyAsyncGen_Fini(PyThreadState *); /* other API */ diff --git a/Include/internal/pycore_interp.h b/Include/internal/pycore_interp.h index 1a155c79963..9843f02fc5e 100644 --- a/Include/internal/pycore_interp.h +++ b/Include/internal/pycore_interp.h @@ -68,6 +68,13 @@ typedef struct PyThreadStateImpl { // semi-public fields are in PyThreadState PyThreadState tstate; + struct _Py_float_state float_state; + struct _Py_tuple_state tuple; + struct _Py_list_state list; + struct _Py_dict_thread_state dict_state; + struct _Py_async_gen_state async_gen; + struct _Py_context_state context; + struct brc_state brc; struct qsbr *qsbr; @@ -194,14 +201,9 @@ struct _is { uint8_t active_code_watchers; struct _Py_unicode_state unicode; - struct _Py_float_state float_state; struct _Py_long_state long_state; - struct _Py_tuple_state tuple; - struct _Py_list_state list; struct _Py_dict_state dict_state; - struct _Py_async_gen_state async_gen; - struct _Py_context_state context; struct _Py_exc_state exc_state; struct ast_state ast; diff --git a/Include/internal/pycore_list.h b/Include/internal/pycore_list.h index dc88305a151..86edc81beae 100644 --- a/Include/internal/pycore_list.h +++ b/Include/internal/pycore_list.h @@ -14,7 +14,7 @@ extern "C" { /* runtime lifecycle */ -extern void _PyList_Fini(PyInterpreterState *); +extern void _PyList_Fini(PyThreadState *); /* other API */ diff --git a/Include/internal/pycore_pystate.h b/Include/internal/pycore_pystate.h index 1243531c9b0..cbfe3193eda 100644 --- a/Include/internal/pycore_pystate.h +++ b/Include/internal/pycore_pystate.h @@ -106,6 +106,11 @@ _PyThreadState_GET(void) return _Py_current_tstate; #endif } +static inline PyThreadStateImpl* +_PyThreadStateImpl_GET(void) +{ + return (PyThreadStateImpl *)_PyThreadState_GET(); +} static inline void _PyThreadState_SET(PyThreadState *tstate) diff --git a/Include/internal/pycore_tuple.h b/Include/internal/pycore_tuple.h index edc70843b57..29556027f87 100644 --- a/Include/internal/pycore_tuple.h +++ b/Include/internal/pycore_tuple.h @@ -15,7 +15,7 @@ extern "C" { extern PyStatus _PyTuple_InitGlobalObjects(PyInterpreterState *); extern PyStatus _PyTuple_InitTypes(PyInterpreterState *); -extern void _PyTuple_Fini(PyInterpreterState *); +extern void _PyTuple_Fini(PyThreadState *); /* other API */ diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c index 879a60d5880..79ae87ddec1 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -1359,20 +1359,32 @@ delete_garbage(PyThreadState *tstate, GCState *gcstate, } } +static void +clear_freelists(PyThreadState *tstate) +{ + _PyTuple_ClearFreeList(tstate); + _PyFloat_ClearFreeList(tstate); + _PyList_ClearFreeList(tstate); + _PyDict_ClearFreeList(tstate); + _PyAsyncGen_ClearFreeLists(tstate); + _PyContext_ClearFreeList(tstate); +} + /* Clear all free lists * All free lists are cleared during the collection of the highest generation. * Allocated items in the free list may keep a pymalloc arena occupied. * Clearing the free lists may give back memory to the OS earlier. */ static void -clear_freelists(PyInterpreterState *interp) +clear_all_freelists(PyInterpreterState *interp) { - _PyTuple_ClearFreeList(interp); - _PyFloat_ClearFreeList(interp); - _PyList_ClearFreeList(interp); - _PyDict_ClearFreeList(interp); - _PyAsyncGen_ClearFreeLists(interp); - _PyContext_ClearFreeList(interp); + HEAD_LOCK(&_PyRuntime); + PyThreadState *tstate = interp->threads.head; + while (tstate != NULL) { + clear_freelists(tstate); + tstate = tstate->next; + } + HEAD_UNLOCK(&_PyRuntime); } /* Deduce which objects among "base" are unreachable from outside the list @@ -1644,6 +1656,12 @@ gc_collect_main(PyThreadState *tstate, int generation, _PyGC_Reason reason) PyGC_Head final_unreachable; handle_resurrected_objects(&unreachable, &final_unreachable); + /* Clear free list only during the collection of the highest + * generation */ + if (generation == NUM_GENERATIONS-1) { + clear_all_freelists(tstate->interp); + } + _PyRuntimeState_StartTheWorld(&_PyRuntime); /* Call tp_clear on objects in the final_unreachable set. This will cause @@ -1653,6 +1671,12 @@ gc_collect_main(PyThreadState *tstate, int generation, _PyGC_Reason reason) m += gc_list_size(&final_unreachable); delete_garbage(tstate, gcstate, &final_unreachable); + if (reason == GC_REASON_MANUAL) { + // Clear this thread's freelists again after deleting garbage + // for more precise block accounting when calling gc.collect(). + clear_freelists(tstate); + } + /* Collect statistics on uncollectable objects found and print * debugging information. */ for (gc = GC_NEXT(&finalizers); gc != &finalizers; gc = GC_NEXT(gc)) { @@ -1673,12 +1697,6 @@ gc_collect_main(PyThreadState *tstate, int generation, _PyGC_Reason reason) */ handle_legacy_finalizers(tstate, gcstate, &finalizers); - /* Clear free list only during the collection of the highest - * generation */ - if (generation == NUM_GENERATIONS-1) { - clear_freelists(tstate->interp); - } - if (_PyErr_Occurred(tstate)) { if (reason == GC_REASON_SHUTDOWN) { _PyErr_Clear(tstate); diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 99e34f01229..e8030315880 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -246,12 +246,17 @@ get_dict_state(void) return &interp->dict_state; } +static struct _Py_dict_thread_state * +get_dict_thread_state(void) +{ + return &_PyThreadStateImpl_GET()->dict_state; +} void -_PyDict_ClearFreeList(PyInterpreterState *interp) +_PyDict_ClearFreeList(PyThreadState *tstate) { #if PyDict_MAXFREELIST > 0 - struct _Py_dict_state *state = &interp->dict_state; + struct _Py_dict_thread_state *state = &((PyThreadStateImpl *)tstate)->dict_state; while (state->numfree) { PyDictObject *op = state->free_list[--state->numfree]; assert(PyDict_CheckExact(op)); @@ -263,17 +268,16 @@ _PyDict_ClearFreeList(PyInterpreterState *interp) #endif } - void -_PyDict_Fini(PyInterpreterState *interp) +_PyDict_Fini(PyThreadState *tstate) { - _PyDict_ClearFreeList(interp); - struct _Py_dict_state *state = &interp->dict_state; + _PyDict_ClearFreeList(tstate); #if defined(Py_DEBUG) && PyDict_MAXFREELIST > 0 + struct _Py_dict_thread_state *state = &((PyThreadStateImpl *)tstate)->dict_state; state->numfree = -1; state->keys_numfree = -1; #endif - PyDictSharedKeysObject **ptr = &state->tracked_shared_keys; + PyDictSharedKeysObject **ptr = &tstate->interp->dict_state.tracked_shared_keys; PyDictSharedKeysObject *value; while ((value = *ptr) != NULL) { *ptr = value->next; @@ -293,7 +297,7 @@ void _PyDict_DebugMallocStats(FILE *out) { #if PyDict_MAXFREELIST > 0 - struct _Py_dict_state *state = get_dict_state(); + struct _Py_dict_thread_state *state = get_dict_thread_state(); _PyDebugAllocatorStats(out, "free PyDictObject", state->numfree, sizeof(PyDictObject)); #endif @@ -636,8 +640,7 @@ new_keys_object(uint8_t log2_size, bool unicode) } #if PyDict_MAXFREELIST > 0 - PyThreadState *tstate = _PyThreadState_GET(); - struct _Py_dict_state *state = &tstate->interp->dict_state; + struct _Py_dict_thread_state *state = get_dict_thread_state(); #ifdef Py_DEBUG // new_keys_object() must not be called after _PyDict_Fini() assert(state->keys_numfree != -1); @@ -700,7 +703,7 @@ free_keys_object(PyDictKeysObject *keys, bool use_qsbr) return; } #if PyDict_MAXFREELIST > 0 - struct _Py_dict_state *state = get_dict_state(); + struct _Py_dict_thread_state *state = get_dict_thread_state(); #ifdef Py_DEBUG // free_keys_object() must not be called after _PyDict_Fini() assert(state->keys_numfree != -1); @@ -760,7 +763,7 @@ new_dict(PyDictKeysObject *keys, PyDictValues *values, Py_ssize_t used, int free PyDictObject *mp; assert(keys != NULL); #if PyDict_MAXFREELIST > 0 - struct _Py_dict_state *state = get_dict_state(); + struct _Py_dict_thread_state *state = get_dict_thread_state(); #ifdef Py_DEBUG // new_dict() must not be called after _PyDict_Fini() assert(state->numfree != -1); @@ -1809,7 +1812,7 @@ dictresize(PyDictObject *mp, uint8_t log2_newsize, int unicode) ASSERT_CONSISTENT(mp); if (oldkeys != Py_EMPTY_KEYS && oldkeys->dk_kind != DICT_KEYS_SPLIT) { #if PyDict_MAXFREELIST > 0 - struct _Py_dict_state *state = get_dict_state(); + struct _Py_dict_thread_state *state = get_dict_thread_state(); #ifdef Py_DEBUG // dictresize() must not be called after _PyDict_Fini() assert(state->keys_numfree != -1); @@ -2703,7 +2706,7 @@ dict_dealloc(PyDictObject *mp) free_keys_object(keys, false); } #if PyDict_MAXFREELIST > 0 - struct _Py_dict_state *state = get_dict_state(); + struct _Py_dict_thread_state *state = get_dict_thread_state(); #ifdef Py_DEBUG // new_dict() must not be called after _PyDict_Fini() assert(state->numfree != -1); diff --git a/Objects/floatobject.c b/Objects/floatobject.c index 912b742f797..f14f69aed06 100644 --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -34,8 +34,7 @@ class float "PyObject *" "&PyFloat_Type" static struct _Py_float_state * get_float_state(void) { - PyInterpreterState *interp = _PyInterpreterState_GET(); - return &interp->float_state; + return &_PyThreadStateImpl_GET()->float_state; } #endif @@ -2012,10 +2011,10 @@ _PyFloat_InitTypes(PyInterpreterState *interp) } void -_PyFloat_ClearFreeList(PyInterpreterState *interp) +_PyFloat_ClearFreeList(PyThreadState *tstate) { #if PyFloat_MAXFREELIST > 0 - struct _Py_float_state *state = &interp->float_state; + struct _Py_float_state *state = &((PyThreadStateImpl *)tstate)->float_state; PyFloatObject *f = state->free_list; while (f != NULL) { PyFloatObject *next = (PyFloatObject*) Py_TYPE(f); @@ -2028,11 +2027,11 @@ _PyFloat_ClearFreeList(PyInterpreterState *interp) } void -_PyFloat_Fini(PyInterpreterState *interp) +_PyFloat_Fini(PyThreadState *tstate) { - _PyFloat_ClearFreeList(interp); + _PyFloat_ClearFreeList(tstate); #if defined(Py_DEBUG) && PyFloat_MAXFREELIST > 0 - struct _Py_float_state *state = &interp->float_state; + struct _Py_float_state *state = &((PyThreadStateImpl *)tstate)->float_state; state->numfree = -1; #endif } diff --git a/Objects/genobject.c b/Objects/genobject.c index 2adb1e4bf30..69763272bf4 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -1601,8 +1601,7 @@ PyTypeObject PyAsyncGen_Type = { static struct _Py_async_gen_state * get_async_gen_state(void) { - PyInterpreterState *interp = _PyInterpreterState_GET(); - return &interp->async_gen; + return &((PyThreadStateImpl *)_PyThreadState_GET())->async_gen; } #endif @@ -1625,10 +1624,10 @@ PyAsyncGen_New(PyFrameObject *f, PyObject *name, PyObject *qualname) void -_PyAsyncGen_ClearFreeLists(PyInterpreterState *interp) +_PyAsyncGen_ClearFreeLists(PyThreadState *tstate) { #if _PyAsyncGen_MAXFREELIST > 0 - struct _Py_async_gen_state *state = &interp->async_gen; + struct _Py_async_gen_state *state = &((PyThreadStateImpl *)tstate)->async_gen; while (state->value_numfree) { _PyAsyncGenWrappedValue *o; @@ -1647,11 +1646,11 @@ _PyAsyncGen_ClearFreeLists(PyInterpreterState *interp) } void -_PyAsyncGen_Fini(PyInterpreterState *interp) +_PyAsyncGen_Fini(PyThreadState *tstate) { - _PyAsyncGen_ClearFreeLists(interp); + _PyAsyncGen_ClearFreeLists(tstate); #if defined(Py_DEBUG) && _PyAsyncGen_MAXFREELIST > 0 - struct _Py_async_gen_state *state = &interp->async_gen; + struct _Py_async_gen_state *state = &((PyThreadStateImpl *)tstate)->async_gen; state->value_numfree = -1; state->asend_numfree = -1; #endif @@ -1979,7 +1978,7 @@ _PyAsyncGenValueWrapperNew(PyThreadState *tstate, PyObject *val) assert(val); #if _PyAsyncGen_MAXFREELIST > 0 - struct _Py_async_gen_state *state = &tstate->interp->async_gen; + struct _Py_async_gen_state *state = &((PyThreadStateImpl *)tstate)->async_gen; #ifdef Py_DEBUG // _PyAsyncGenValueWrapperNew() must not be called after _PyAsyncGen_Fini() assert(state->value_numfree != -1); diff --git a/Objects/listobject.c b/Objects/listobject.c index a840c72aa5e..2816428cc9b 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -25,8 +25,7 @@ _Py_DECLARE_STR(list_err, "list index out of range"); static struct _Py_list_state * get_list_state(void) { - PyInterpreterState *interp = _PyInterpreterState_GET(); - return &interp->list; + return &((PyThreadStateImpl *)_PyThreadState_GET())->list; } #endif @@ -178,10 +177,10 @@ list_resize(PyListObject *self, Py_ssize_t newsize) } void -_PyList_ClearFreeList(PyInterpreterState *interp) +_PyList_ClearFreeList(PyThreadState *tstate) { #if PyList_MAXFREELIST > 0 - struct _Py_list_state *state = &interp->list; + struct _Py_list_state *state = &((PyThreadStateImpl *)tstate)->list; while (state->numfree) { PyListObject *op = state->free_list[--state->numfree]; assert(PyList_CheckExact(op)); @@ -191,11 +190,11 @@ _PyList_ClearFreeList(PyInterpreterState *interp) } void -_PyList_Fini(PyInterpreterState *interp) +_PyList_Fini(PyThreadState *tstate) { - _PyList_ClearFreeList(interp); + _PyList_ClearFreeList(tstate); #if defined(Py_DEBUG) && PyList_MAXFREELIST > 0 - struct _Py_list_state *state = &interp->list; + struct _Py_list_state *state = &((PyThreadStateImpl *)tstate)->list; state->numfree = -1; #endif } diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c index d9dcc4f494a..5434c1eb5d6 100644 --- a/Objects/tupleobject.c +++ b/Objects/tupleobject.c @@ -979,18 +979,18 @@ _PyTuple_InitTypes(PyInterpreterState *interp) return _PyStatus_OK(); } -static void maybe_freelist_clear(PyInterpreterState *, int); +static void maybe_freelist_clear(PyThreadState *, int); void -_PyTuple_Fini(PyInterpreterState *interp) +_PyTuple_Fini(PyThreadState *tstate) { - maybe_freelist_clear(interp, 1); + maybe_freelist_clear(tstate, 1); } void -_PyTuple_ClearFreeList(PyInterpreterState *interp) +_PyTuple_ClearFreeList(PyThreadState *tstate) { - maybe_freelist_clear(interp, 0); + maybe_freelist_clear(tstate, 0); } /*********************** Tuple Iterator **************************/ @@ -1137,14 +1137,14 @@ tuple_iter(PyObject *seq) * freelists * *************/ -#define STATE (interp->tuple) +#define STATE (((PyThreadStateImpl *)tstate)->tuple) #define FREELIST_FINALIZED (STATE.numfree[0] < 0) static inline PyTupleObject * maybe_freelist_pop(Py_ssize_t size) { #if PyTuple_NFREELISTS > 0 - PyInterpreterState *interp = _PyInterpreterState_GET(); + PyThreadState *tstate = _PyThreadState_GET(); #ifdef Py_DEBUG /* maybe_freelist_pop() must not be called after maybe_freelist_fini(). */ assert(!FREELIST_FINALIZED); @@ -1182,7 +1182,7 @@ static inline int maybe_freelist_push(PyTupleObject *op) { #if PyTuple_NFREELISTS > 0 - PyInterpreterState *interp = _PyInterpreterState_GET(); + PyThreadState *tstate = _PyThreadState_GET(); #ifdef Py_DEBUG /* maybe_freelist_push() must not be called after maybe_freelist_fini(). */ assert(!FREELIST_FINALIZED); @@ -1208,7 +1208,7 @@ maybe_freelist_push(PyTupleObject *op) } static void -maybe_freelist_clear(PyInterpreterState *interp, int fini) +maybe_freelist_clear(PyThreadState *tstate, int fini) { #if PyTuple_NFREELISTS > 0 for (Py_ssize_t i = 0; i < PyTuple_NFREELISTS; i++) { @@ -1229,7 +1229,8 @@ void _PyTuple_DebugMallocStats(FILE *out) { #if PyTuple_NFREELISTS > 0 - PyInterpreterState *interp = _PyInterpreterState_GET(); + PyThreadState *tstate = _PyThreadState_GET(); + // PyInterpreterState *interp = _PyInterpreterState_GET(); for (int i = 0; i < PyTuple_NFREELISTS; i++) { int len = i + 1; char buf[128]; diff --git a/Python/context.c b/Python/context.c index 5d385508405..c501f6825da 100644 --- a/Python/context.c +++ b/Python/context.c @@ -68,8 +68,7 @@ contextvar_del(PyContextVar *var); static struct _Py_context_state * get_context_state(void) { - PyInterpreterState *interp = _PyInterpreterState_GET(); - return &interp->context; + return &_PyThreadStateImpl_GET()->context; } #endif @@ -1275,10 +1274,10 @@ get_token_missing(void) void -_PyContext_ClearFreeList(PyInterpreterState *interp) +_PyContext_ClearFreeList(PyThreadState *tstate) { #if PyContext_MAXFREELIST > 0 - struct _Py_context_state *state = &interp->context; + struct _Py_context_state *state = &((PyThreadStateImpl *)tstate)->context; for (; state->numfree; state->numfree--) { PyContext *ctx = state->freelist; state->freelist = (PyContext *)ctx->ctx_weakreflist; @@ -1290,11 +1289,11 @@ _PyContext_ClearFreeList(PyInterpreterState *interp) void -_PyContext_Fini(PyInterpreterState *interp) +_PyContext_Fini(PyThreadState *tstate) { - _PyContext_ClearFreeList(interp); + _PyContext_ClearFreeList(tstate); #if defined(Py_DEBUG) && PyContext_MAXFREELIST > 0 - struct _Py_context_state *state = &interp->context; + struct _Py_context_state *state = &((PyThreadStateImpl *)tstate)->context; state->numfree = -1; #endif } diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 3809aa62ef5..a912cb1b0dc 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -1733,13 +1733,14 @@ flush_std_files(void) static void -finalize_interp_types(PyInterpreterState *interp) +finalize_interp_types(PyThreadState *tstate) { + PyInterpreterState *interp = tstate->interp; _PyUnicode_FiniTypes(interp); _PySys_Fini(interp); _PyExc_Fini(interp); - _PyAsyncGen_Fini(interp); - _PyContext_Fini(interp); + _PyAsyncGen_Fini(tstate); + _PyContext_Fini(tstate); _PyFloat_FiniType(interp); _PyLong_FiniTypes(interp); _PyThread_FiniType(interp); @@ -1752,14 +1753,14 @@ finalize_interp_types(PyInterpreterState *interp) // a dict internally. _PyUnicode_ClearInterned(interp); - _PyDict_Fini(interp); - _PyList_Fini(interp); - _PyTuple_Fini(interp); + _PyDict_Fini(tstate); + _PyList_Fini(tstate); + _PyTuple_Fini(tstate); _PySlice_Fini(interp); _PyUnicode_Fini(interp); - _PyFloat_Fini(interp); + _PyFloat_Fini(tstate); #ifdef Py_DEBUG _PyStaticObjects_CheckRefcnt(interp); #endif @@ -1795,7 +1796,7 @@ finalize_interp_clear(PyThreadState *tstate) _PyPerfTrampoline_Fini(); } - finalize_interp_types(tstate->interp); + finalize_interp_types(tstate); } diff --git a/Python/pystate.c b/Python/pystate.c index 2b9f5b053b4..dc529cd096a 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -1398,6 +1398,16 @@ PyThreadState_Clear(PyThreadState *tstate) Py_CLEAR(tstate->async_gen_finalizer); Py_CLEAR(tstate->context); + + /* Clear the thread's free lists. If thread is still active we may still, + use the free-lists a bit before they are cleared a final timem in + finalize_interp_types. */ + _PyTuple_ClearFreeList(tstate); + _PyFloat_ClearFreeList(tstate); + _PyList_ClearFreeList(tstate); + _PyDict_ClearFreeList(tstate); + _PyAsyncGen_ClearFreeLists(tstate); + _PyContext_ClearFreeList(tstate); }