From ae00a5a88534fd45939f86c12e038da9fa6f9ed6 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 29 Apr 2020 02:29:20 +0200 Subject: [PATCH] bpo-40428: Remove PyTuple_ClearFreeList() function (GH-19769) Remove the following function from the C API: * PyAsyncGen_ClearFreeLists() * PyContext_ClearFreeList() * PyDict_ClearFreeList() * PyFloat_ClearFreeList() * PyFrame_ClearFreeList() * PyList_ClearFreeList() * PySet_ClearFreeList() * PyTuple_ClearFreeList() Make these functions private, move them to the internal C API and change their return type to void. Call explicitly PyGC_Collect() to free all free lists. Note: PySet_ClearFreeList() did nothing. --- Doc/whatsnew/3.9.rst | 13 +++++++++++++ Include/context.h | 3 --- Include/cpython/dictobject.h | 2 -- Include/cpython/frameobject.h | 2 -- Include/cpython/listobject.h | 1 - Include/floatobject.h | 3 --- Include/genobject.h | 2 -- Include/internal/pycore_gc.h | 10 ++++++++++ Include/setobject.h | 1 - Include/tupleobject.h | 2 -- .../2020-04-28-23-17-27.bpo-40428.rmtpru.rst | 11 +++++++++++ Modules/gcmodule.c | 16 +++++++--------- Objects/dictobject.c | 11 ++++------- Objects/floatobject.c | 11 ++++------- Objects/frameobject.c | 9 +++------ Objects/genobject.c | 10 +++------- Objects/listobject.c | 11 ++++------- Objects/setobject.c | 6 ------ Objects/tupleobject.c | 18 +++++++----------- PC/python3.def | 2 -- Python/context.c | 13 +++++-------- 21 files changed, 71 insertions(+), 86 deletions(-) create mode 100644 Misc/NEWS.d/next/C API/2020-04-28-23-17-27.bpo-40428.rmtpru.rst diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst index cb3afd59357..e26bd473e61 100644 --- a/Doc/whatsnew/3.9.rst +++ b/Doc/whatsnew/3.9.rst @@ -672,6 +672,19 @@ Build and C API Changes the garbage collector respectively. (Contributed by Pablo Galindo in :issue:`40241`.) +* Remove the following functions from the C API. Call :c:func:`PyGC_Collect` + explicitly to free all free lists. + (Contributed by Victor Stinner in :issue:`40428`.) + + * ``PyAsyncGen_ClearFreeLists()`` + * ``PyContext_ClearFreeList()`` + * ``PyDict_ClearFreeList()`` + * ``PyFloat_ClearFreeList()`` + * ``PyFrame_ClearFreeList()`` + * ``PyList_ClearFreeList()`` + * ``PySet_ClearFreeList()`` + * ``PyTuple_ClearFreeList()`` + Deprecated ========== diff --git a/Include/context.h b/Include/context.h index 619746d501e..4e5007089dd 100644 --- a/Include/context.h +++ b/Include/context.h @@ -73,9 +73,6 @@ PyAPI_FUNC(int) PyContextVar_Reset(PyObject *var, PyObject *token); PyAPI_FUNC(PyObject *) _PyContext_NewHamtForTests(void); -PyAPI_FUNC(int) PyContext_ClearFreeList(void); - - #endif /* !Py_LIMITED_API */ #ifdef __cplusplus diff --git a/Include/cpython/dictobject.h b/Include/cpython/dictobject.h index 64c012a012b..e33a0d156fe 100644 --- a/Include/cpython/dictobject.h +++ b/Include/cpython/dictobject.h @@ -62,8 +62,6 @@ PyObject *_PyDict_Pop_KnownHash(PyObject *, PyObject *, Py_hash_t, PyObject *); PyObject *_PyDict_FromKeys(PyObject *, PyObject *, PyObject *); #define _PyDict_HasSplitTable(d) ((d)->ma_values != NULL) -PyAPI_FUNC(int) PyDict_ClearFreeList(void); - /* Like PyDict_Merge, but override can be 0, 1 or 2. If override is 0, the first occurrence of a key wins, if override is 1, the last occurrence of a key wins, if override is 2, a KeyError with conflicting key as diff --git a/Include/cpython/frameobject.h b/Include/cpython/frameobject.h index e819cefd13c..e32efac5947 100644 --- a/Include/cpython/frameobject.h +++ b/Include/cpython/frameobject.h @@ -75,8 +75,6 @@ PyAPI_FUNC(void) PyFrame_LocalsToFast(PyFrameObject *, int); PyAPI_FUNC(int) PyFrame_FastToLocalsWithError(PyFrameObject *f); PyAPI_FUNC(void) PyFrame_FastToLocals(PyFrameObject *); -PyAPI_FUNC(int) PyFrame_ClearFreeList(void); - PyAPI_FUNC(void) _PyFrame_DebugMallocStats(FILE *out); #ifdef __cplusplus diff --git a/Include/cpython/listobject.h b/Include/cpython/listobject.h index 4b6f2f7741c..74fe3301a7a 100644 --- a/Include/cpython/listobject.h +++ b/Include/cpython/listobject.h @@ -26,7 +26,6 @@ typedef struct { } PyListObject; PyAPI_FUNC(PyObject *) _PyList_Extend(PyListObject *, PyObject *); -PyAPI_FUNC(int) PyList_ClearFreeList(void); PyAPI_FUNC(void) _PyList_DebugMallocStats(FILE *out); /* Macro, trading safety for speed */ diff --git a/Include/floatobject.h b/Include/floatobject.h index 917dfcc2644..e994aa8f29d 100644 --- a/Include/floatobject.h +++ b/Include/floatobject.h @@ -100,9 +100,6 @@ PyAPI_FUNC(double) _PyFloat_Unpack2(const unsigned char *p, int le); PyAPI_FUNC(double) _PyFloat_Unpack4(const unsigned char *p, int le); PyAPI_FUNC(double) _PyFloat_Unpack8(const unsigned char *p, int le); -/* free list api */ -PyAPI_FUNC(int) PyFloat_ClearFreeList(void); - PyAPI_FUNC(void) _PyFloat_DebugMallocStats(FILE* out); /* Format the object based on the format_spec, as defined in PEP 3101 diff --git a/Include/genobject.h b/Include/genobject.h index a7393a9a835..8ffd15646f0 100644 --- a/Include/genobject.h +++ b/Include/genobject.h @@ -91,8 +91,6 @@ PyAPI_FUNC(PyObject *) PyAsyncGen_New(PyFrameObject *, PyObject *_PyAsyncGenValueWrapperNew(PyObject *); -int PyAsyncGen_ClearFreeLists(void); - #endif #undef _PyGenObject_HEAD diff --git a/Include/internal/pycore_gc.h b/Include/internal/pycore_gc.h index 62b8800e249..0511eea779a 100644 --- a/Include/internal/pycore_gc.h +++ b/Include/internal/pycore_gc.h @@ -163,6 +163,16 @@ struct _gc_runtime_state { PyAPI_FUNC(void) _PyGC_InitState(struct _gc_runtime_state *); + +// Functions to clear types free lists +extern void _PyFrame_ClearFreeList(void); +extern void _PyTuple_ClearFreeList(void); +extern void _PyFloat_ClearFreeList(void); +extern void _PyList_ClearFreeList(void); +extern void _PyDict_ClearFreeList(void); +extern void _PyAsyncGen_ClearFreeLists(void); +extern void _PyContext_ClearFreeList(void); + #ifdef __cplusplus } #endif diff --git a/Include/setobject.h b/Include/setobject.h index 05a097eba7f..119619ebe72 100644 --- a/Include/setobject.h +++ b/Include/setobject.h @@ -70,7 +70,6 @@ PyAPI_DATA(PyObject *) _PySet_Dummy; PyAPI_FUNC(int) _PySet_NextEntry(PyObject *set, Py_ssize_t *pos, PyObject **key, Py_hash_t *hash); PyAPI_FUNC(int) _PySet_Update(PyObject *set, PyObject *iterable); -PyAPI_FUNC(int) PySet_ClearFreeList(void); #endif /* Section excluded by Py_LIMITED_API */ diff --git a/Include/tupleobject.h b/Include/tupleobject.h index d3504b0501f..e796a320192 100644 --- a/Include/tupleobject.h +++ b/Include/tupleobject.h @@ -34,8 +34,6 @@ PyAPI_FUNC(int) PyTuple_SetItem(PyObject *, Py_ssize_t, PyObject *); PyAPI_FUNC(PyObject *) PyTuple_GetSlice(PyObject *, Py_ssize_t, Py_ssize_t); PyAPI_FUNC(PyObject *) PyTuple_Pack(Py_ssize_t, ...); -PyAPI_FUNC(int) PyTuple_ClearFreeList(void); - #ifndef Py_LIMITED_API # define Py_CPYTHON_TUPLEOBJECT_H # include "cpython/tupleobject.h" diff --git a/Misc/NEWS.d/next/C API/2020-04-28-23-17-27.bpo-40428.rmtpru.rst b/Misc/NEWS.d/next/C API/2020-04-28-23-17-27.bpo-40428.rmtpru.rst new file mode 100644 index 00000000000..f8710efb6c3 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2020-04-28-23-17-27.bpo-40428.rmtpru.rst @@ -0,0 +1,11 @@ +Remove the following functions from the C API. Call :c:func:`PyGC_Collect` +explicitly to free all free lists. + +* ``PyAsyncGen_ClearFreeLists()`` +* ``PyContext_ClearFreeList()`` +* ``PyDict_ClearFreeList()`` +* ``PyFloat_ClearFreeList()`` +* ``PyFrame_ClearFreeList()`` +* ``PyList_ClearFreeList()`` +* ``PySet_ClearFreeList()`` +* ``PyTuple_ClearFreeList()`` diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c index 5727820f09b..56dcb101e00 100644 --- a/Modules/gcmodule.c +++ b/Modules/gcmodule.c @@ -30,7 +30,6 @@ #include "pycore_object.h" #include "pycore_pyerrors.h" #include "pycore_pystate.h" // _PyThreadState_GET() -#include "frameobject.h" // PyFrame_ClearFreeList #include "pydtrace.h" #include "pytime.h" // _PyTime_GetMonotonicClock() @@ -1026,14 +1025,13 @@ delete_garbage(PyThreadState *tstate, GCState *gcstate, static void clear_freelists(void) { - (void)PyFrame_ClearFreeList(); - (void)PyTuple_ClearFreeList(); - (void)PyFloat_ClearFreeList(); - (void)PyList_ClearFreeList(); - (void)PyDict_ClearFreeList(); - (void)PySet_ClearFreeList(); - (void)PyAsyncGen_ClearFreeLists(); - (void)PyContext_ClearFreeList(); + _PyFrame_ClearFreeList(); + _PyTuple_ClearFreeList(); + _PyFloat_ClearFreeList(); + _PyList_ClearFreeList(); + _PyDict_ClearFreeList(); + _PyAsyncGen_ClearFreeLists(); + _PyContext_ClearFreeList(); } // Show stats for objects in each generations diff --git a/Objects/dictobject.c b/Objects/dictobject.c index 8f9d4e7b731..9c35f3c3f14 100644 --- a/Objects/dictobject.c +++ b/Objects/dictobject.c @@ -257,20 +257,17 @@ static int numfreekeys = 0; #include "clinic/dictobject.c.h" -int -PyDict_ClearFreeList(void) +void +_PyDict_ClearFreeList(void) { - PyDictObject *op; - int ret = numfree + numfreekeys; while (numfree) { - op = free_list[--numfree]; + PyDictObject *op = free_list[--numfree]; assert(PyDict_CheckExact(op)); PyObject_GC_Del(op); } while (numfreekeys) { PyObject_FREE(keys_free_list[--numfreekeys]); } - return ret; } /* Print summary info about the state of the optimized allocator */ @@ -285,7 +282,7 @@ _PyDict_DebugMallocStats(FILE *out) void _PyDict_Fini(void) { - PyDict_ClearFreeList(); + _PyDict_ClearFreeList(); } #define DK_SIZE(dk) ((dk)->dk_size) diff --git a/Objects/floatobject.c b/Objects/floatobject.c index 04f968e56b1..faa02f2f057 100644 --- a/Objects/floatobject.c +++ b/Objects/floatobject.c @@ -1998,25 +1998,22 @@ _PyFloat_Init(void) return 1; } -int -PyFloat_ClearFreeList(void) +void +_PyFloat_ClearFreeList(void) { PyFloatObject *f = free_list, *next; - int i = numfree; - while (f) { + for (; f; f = next) { next = (PyFloatObject*) Py_TYPE(f); PyObject_FREE(f); - f = next; } free_list = NULL; numfree = 0; - return i; } void _PyFloat_Fini(void) { - (void)PyFloat_ClearFreeList(); + _PyFloat_ClearFreeList(); } /* Print summary info about the state of the optimized allocator */ diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 533186bc046..6d288b5b059 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -1200,11 +1200,9 @@ PyFrame_LocalsToFast(PyFrameObject *f, int clear) } /* Clear out the free list */ -int -PyFrame_ClearFreeList(void) +void +_PyFrame_ClearFreeList(void) { - int freelist_size = numfree; - while (free_list != NULL) { PyFrameObject *f = free_list; free_list = free_list->f_back; @@ -1212,13 +1210,12 @@ PyFrame_ClearFreeList(void) --numfree; } assert(numfree == 0); - return freelist_size; } void _PyFrame_Fini(void) { - (void)PyFrame_ClearFreeList(); + _PyFrame_ClearFreeList(); } /* Print summary info about the state of the optimized allocator */ diff --git a/Objects/genobject.c b/Objects/genobject.c index 071def8a25a..6e36690b651 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -1429,11 +1429,9 @@ PyAsyncGen_New(PyFrameObject *f, PyObject *name, PyObject *qualname) } -int -PyAsyncGen_ClearFreeLists(void) +void +_PyAsyncGen_ClearFreeLists(void) { - int ret = ag_value_freelist_free + ag_asend_freelist_free; - while (ag_value_freelist_free) { _PyAsyncGenWrappedValue *o; o = ag_value_freelist[--ag_value_freelist_free]; @@ -1447,14 +1445,12 @@ PyAsyncGen_ClearFreeLists(void) assert(Py_IS_TYPE(o, &_PyAsyncGenASend_Type)); PyObject_GC_Del(o); } - - return ret; } void _PyAsyncGen_Fini(void) { - PyAsyncGen_ClearFreeLists(); + _PyAsyncGen_ClearFreeLists(); } diff --git a/Objects/listobject.c b/Objects/listobject.c index 7d2f006617b..904bea317c9 100644 --- a/Objects/listobject.c +++ b/Objects/listobject.c @@ -103,23 +103,20 @@ list_preallocate_exact(PyListObject *self, Py_ssize_t size) static PyListObject *free_list[PyList_MAXFREELIST]; static int numfree = 0; -int -PyList_ClearFreeList(void) +void +_PyList_ClearFreeList(void) { - PyListObject *op; - int ret = numfree; while (numfree) { - op = free_list[--numfree]; + PyListObject *op = free_list[--numfree]; assert(PyList_CheckExact(op)); PyObject_GC_Del(op); } - return ret; } void _PyList_Fini(void) { - PyList_ClearFreeList(); + _PyList_ClearFreeList(); } /* Print summary info about the state of the optimized allocator */ diff --git a/Objects/setobject.c b/Objects/setobject.c index 8452546008b..bbe013bcfac 100644 --- a/Objects/setobject.c +++ b/Objects/setobject.c @@ -2384,12 +2384,6 @@ PySet_Add(PyObject *anyset, PyObject *key) return set_add_key((PySetObject *)anyset, key); } -int -PySet_ClearFreeList(void) -{ - return 0; -} - void _PySet_Fini(void) { diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c index b65b8abc280..f8648d24f1c 100644 --- a/Objects/tupleobject.c +++ b/Objects/tupleobject.c @@ -955,26 +955,22 @@ _PyTuple_Resize(PyObject **pv, Py_ssize_t newsize) return 0; } -int -PyTuple_ClearFreeList(void) +void +_PyTuple_ClearFreeList(void) { - int freelist_size = 0; #if PyTuple_MAXSAVESIZE > 0 - int i; - for (i = 1; i < PyTuple_MAXSAVESIZE; i++) { - PyTupleObject *p, *q; - p = free_list[i]; - freelist_size += numfree[i]; + for (Py_ssize_t i = 1; i < PyTuple_MAXSAVESIZE; i++) { + PyTupleObject *p = free_list[i]; free_list[i] = NULL; numfree[i] = 0; while (p) { - q = p; + PyTupleObject *q = p; p = (PyTupleObject *)(p->ob_item[0]); PyObject_GC_Del(q); } } + // the empty tuple singleton is only cleared by _PyTuple_Fini() #endif - return freelist_size; } void @@ -985,7 +981,7 @@ _PyTuple_Fini(void) * rely on the fact that an empty tuple is a singleton. */ Py_CLEAR(free_list[0]); - (void)PyTuple_ClearFreeList(); + _PyTuple_ClearFreeList(); #endif } diff --git a/PC/python3.def b/PC/python3.def index 083384e30f6..1521ac738c0 100644 --- a/PC/python3.def +++ b/PC/python3.def @@ -35,7 +35,6 @@ EXPORTS PyBytes_Size=python39.PyBytes_Size PyBytes_Type=python39.PyBytes_Type DATA PyCFunction_Call=python39.PyCFunction_Call - PyCFunction_ClearFreeList=python39.PyCFunction_ClearFreeList PyCFunction_GetFlags=python39.PyCFunction_GetFlags PyCFunction_GetFunction=python39.PyCFunction_GetFunction PyCFunction_GetSelf=python39.PyCFunction_GetSelf @@ -584,7 +583,6 @@ EXPORTS PyTraceBack_Print=python39.PyTraceBack_Print PyTraceBack_Type=python39.PyTraceBack_Type DATA PyTupleIter_Type=python39.PyTupleIter_Type DATA - PyTuple_ClearFreeList=python39.PyTuple_ClearFreeList PyTuple_GetItem=python39.PyTuple_GetItem PyTuple_GetSlice=python39.PyTuple_GetSlice PyTuple_New=python39.PyTuple_New diff --git a/Python/context.c b/Python/context.c index f0217f28018..bacc7010c45 100644 --- a/Python/context.c +++ b/Python/context.c @@ -1270,18 +1270,15 @@ get_token_missing(void) /////////////////////////// -int -PyContext_ClearFreeList(void) +void +_PyContext_ClearFreeList(void) { - int size = ctx_freelist_len; - while (ctx_freelist_len) { + for (; ctx_freelist_len; ctx_freelist_len--) { PyContext *ctx = ctx_freelist; ctx_freelist = (PyContext *)ctx->ctx_weakreflist; ctx->ctx_weakreflist = NULL; PyObject_GC_Del(ctx); - ctx_freelist_len--; } - return size; } @@ -1289,8 +1286,8 @@ void _PyContext_Fini(void) { Py_CLEAR(_token_missing); - (void)PyContext_ClearFreeList(); - (void)_PyHamt_Fini(); + _PyContext_ClearFreeList(); + _PyHamt_Fini(); }