Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gh-111968: Refactor _PyXXX_Fini to integrate with _PyObject_ClearFreeLists #114899

Merged
merged 14 commits into from
Feb 10, 2024
1 change: 0 additions & 1 deletion Include/internal/pycore_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ extern PyTypeObject _PyContextTokenMissing_Type;
/* runtime lifecycle */

PyStatus _PyContext_Init(PyInterpreterState *);
void _PyContext_Fini(_PyFreeListState *);


/* other API */
Expand Down
1 change: 0 additions & 1 deletion Include/internal/pycore_floatobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ extern "C" {

extern void _PyFloat_InitState(PyInterpreterState *);
extern PyStatus _PyFloat_InitTypes(PyInterpreterState *);
extern void _PyFloat_Fini(_PyFreeListState *);
extern void _PyFloat_FiniType(PyInterpreterState *);


Expand Down
10 changes: 10 additions & 0 deletions Include/internal/pycore_freelist.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,16 @@ typedef struct _Py_freelist_state {
struct _Py_object_stack_state object_stacks;
} _PyFreeListState;

extern void _PyObject_ClearFreeLists(_PyFreeListState *state, int is_finalization);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this API is not a domain-specific API, I located all ClearFreeList API into here :)

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_ClearFreeList(_PyFreeListState *state, int is_finalization);
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 _PyObjectStackChunk_ClearFreeList(_PyFreeListState *state, int is_finalization);

#ifdef __cplusplus
}
#endif
Expand Down
8 changes: 0 additions & 8 deletions Include/internal/pycore_gc.h
Original file line number Diff line number Diff line change
Expand Up @@ -262,14 +262,6 @@ extern PyObject *_PyGC_GetReferrers(PyInterpreterState *interp, PyObject *objs);

// Functions to clear types free lists
extern void _PyGC_ClearAllFreeLists(PyInterpreterState *interp);
extern void _Py_ClearFreeLists(_PyFreeListState *state, int is_finalization);
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(_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);
extern void _Py_RunGC(PyThreadState *tstate);

Expand Down
4 changes: 0 additions & 4 deletions Include/internal/pycore_genobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,6 @@ extern PyTypeObject _PyCoroWrapper_Type;
extern PyTypeObject _PyAsyncGenWrappedValue_Type;
extern PyTypeObject _PyAsyncGenAThrow_Type;

/* runtime lifecycle */

extern void _PyAsyncGen_Fini(_PyFreeListState *);

#ifdef __cplusplus
}
#endif
Expand Down
6 changes: 0 additions & 6 deletions Include/internal/pycore_list.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,6 @@ extern "C" {
extern PyObject* _PyList_Extend(PyListObject *, PyObject *);
extern void _PyList_DebugMallocStats(FILE *out);


/* runtime lifecycle */

extern void _PyList_Fini(_PyFreeListState *);


#define _PyList_ITEMS(op) _Py_RVALUE(_PyList_CAST(op)->ob_item)

extern int
Expand Down
3 changes: 0 additions & 3 deletions Include/internal/pycore_object_stack.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,6 @@ _PyObjectStackChunk_New(void);
extern void
_PyObjectStackChunk_Free(_PyObjectStackChunk *);

extern void
_PyObjectStackChunk_ClearFreeList(_PyFreeListState *state, int is_finalization);

// Push an item onto the stack. Return -1 on allocation failure, 0 on success.
static inline int
_PyObjectStack_Push(_PyObjectStack *stack, PyObject *obj)
Expand Down
2 changes: 0 additions & 2 deletions Include/internal/pycore_sliceobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ extern "C" {

/* runtime lifecycle */

extern void _PySlice_Fini(_PyFreeListState *);

extern PyObject *
_PyBuildSlice_ConsumeRefs(PyObject *start, PyObject *stop);

Expand Down
1 change: 0 additions & 1 deletion Include/internal/pycore_tuple.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ extern void _PyTuple_DebugMallocStats(FILE *out);
/* runtime lifecycle */

extern PyStatus _PyTuple_InitGlobalObjects(PyInterpreterState *);
extern void _PyTuple_Fini(_PyFreeListState *);


/* other API */
Expand Down
10 changes: 0 additions & 10 deletions Objects/floatobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -2010,16 +2010,6 @@ _PyFloat_ClearFreeList(_PyFreeListState *freelist_state, int is_finalization)
#endif
}

void
_PyFloat_Fini(_PyFreeListState *state)
{
// With Py_GIL_DISABLED:
// the freelists for the current thread state have already been cleared.
#ifndef Py_GIL_DISABLED
_PyFloat_ClearFreeList(state, 1);
#endif
}

void
_PyFloat_FiniType(PyInterpreterState *interp)
{
Expand Down
11 changes: 0 additions & 11 deletions Objects/genobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -1682,17 +1682,6 @@ _PyAsyncGen_ClearFreeLists(_PyFreeListState *freelist_state, int is_finalization
#endif
}

void
_PyAsyncGen_Fini(_PyFreeListState *state)
{
// With Py_GIL_DISABLED:
// the freelists for the current thread state have already been cleared.
#ifndef Py_GIL_DISABLED
_PyAsyncGen_ClearFreeLists(state, 1);
#endif
}


static PyObject *
async_gen_unwrap_value(PyAsyncGenObject *gen, PyObject *result)
{
Expand Down
10 changes: 0 additions & 10 deletions Objects/listobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -135,16 +135,6 @@ _PyList_ClearFreeList(_PyFreeListState *freelist_state, int is_finalization)
#endif
}

void
_PyList_Fini(_PyFreeListState *state)
{
// With Py_GIL_DISABLED:
// the freelists for the current thread state have already been cleared.
#ifndef Py_GIL_DISABLED
_PyList_ClearFreeList(state, 1);
#endif
}

/* Print summary info about the state of the optimized allocator */
void
_PyList_DebugMallocStats(FILE *out)
Expand Down
15 changes: 15 additions & 0 deletions Objects/object.c
Original file line number Diff line number Diff line change
Expand Up @@ -794,6 +794,21 @@ PyObject_Bytes(PyObject *v)
return PyBytes_FromObject(v);
}

void
_PyObject_ClearFreeLists(_PyFreeListState *state, int is_finalization)
{
// 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);
_PyDict_ClearFreeList(state, is_finalization);
_PyContext_ClearFreeList(state, is_finalization);
_PyAsyncGen_ClearFreeLists(state, is_finalization);
// Only be cleared if is_finalization is true.
_PyObjectStackChunk_ClearFreeList(state, is_finalization);
_PySlice_ClearFreeList(state, is_finalization);
}

/*
def _PyObject_FunctionStr(x):
Expand Down
12 changes: 4 additions & 8 deletions Objects/sliceobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,11 @@ PyObject _Py_EllipsisObject = _PyObject_HEAD_INIT(&PyEllipsis_Type);

/* Slice object implementation */

void _PySlice_ClearCache(_PyFreeListState *state)
void _PySlice_ClearFreeList(_PyFreeListState *state, int is_finalization)
{
if (!is_finalization) {
return;
}
#ifdef WITH_FREELISTS
PySliceObject *obj = state->slices.slice_cache;
if (obj != NULL) {
Expand All @@ -114,13 +117,6 @@ void _PySlice_ClearCache(_PyFreeListState *state)
#endif
}

void _PySlice_Fini(_PyFreeListState *state)
{
#ifdef WITH_FREELISTS
_PySlice_ClearCache(state);
#endif
}

/* start, stop, and step are python objects with None indicating no
index is present.
*/
Expand Down
5 changes: 0 additions & 5 deletions Objects/tupleobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -964,11 +964,6 @@ _PyTuple_Resize(PyObject **pv, Py_ssize_t newsize)

static void maybe_freelist_clear(_PyFreeListState *, int);

void
_PyTuple_Fini(_PyFreeListState *state)
{
maybe_freelist_clear(state, 1);
}

void
_PyTuple_ClearFreeList(_PyFreeListState *state, int is_finalization)
Expand Down
11 changes: 0 additions & 11 deletions Python/context.c
Original file line number Diff line number Diff line change
Expand Up @@ -1284,17 +1284,6 @@ _PyContext_ClearFreeList(_PyFreeListState *freelist_state, int is_finalization)
}


void
_PyContext_Fini(_PyFreeListState *state)
{
// With Py_GIL_DISABLED:
// the freelists for the current thread state have already been cleared.
#ifndef Py_GIL_DISABLED
_PyContext_ClearFreeList(state, 1);
#endif
}


PyStatus
_PyContext_Init(PyInterpreterState *interp)
{
Expand Down
2 changes: 1 addition & 1 deletion Python/gc_free_threading.c
Original file line number Diff line number Diff line change
Expand Up @@ -1679,7 +1679,7 @@ _PyGC_ClearAllFreeLists(PyInterpreterState *interp)
HEAD_LOCK(&_PyRuntime);
_PyThreadStateImpl *tstate = (_PyThreadStateImpl *)interp->threads.head;
while (tstate != NULL) {
_Py_ClearFreeLists(&tstate->freelist_state, 0);
_PyObject_ClearFreeLists(&tstate->freelist_state, 0);
tstate = (_PyThreadStateImpl *)tstate->base.next;
}
HEAD_UNLOCK(&_PyRuntime);
Expand Down
2 changes: 1 addition & 1 deletion Python/gc_gil.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
void
_PyGC_ClearAllFreeLists(PyInterpreterState *interp)
{
_Py_ClearFreeLists(&interp->freelist_state, 0);
_PyObject_ClearFreeLists(&interp->freelist_state, 0);
}

#endif
12 changes: 5 additions & 7 deletions Python/pylifecycle.c
Original file line number Diff line number Diff line change
Expand Up @@ -1782,16 +1782,14 @@ finalize_interp_types(PyInterpreterState *interp)
// a dict internally.
_PyUnicode_ClearInterned(interp);

_PyDict_Fini(interp);
_PyUnicode_Fini(interp);

#ifndef Py_GIL_DISABLED
// With Py_GIL_DISABLED:
// the freelists for the current thread state have already been cleared.
_PyFreeListState *state = _PyFreeListState_GET();
_PyTuple_Fini(state);
_PyList_Fini(state);
_PyFloat_Fini(state);
_PySlice_Fini(state);
_PyContext_Fini(state);
_PyAsyncGen_Fini(state);
_PyObject_ClearFreeLists(state, 1);
#endif

#ifdef Py_DEBUG
_PyStaticObjects_CheckRefcnt(interp);
Expand Down
19 changes: 2 additions & 17 deletions Python/pystate.c
Original file line number Diff line number Diff line change
Expand Up @@ -1458,20 +1458,6 @@ clear_datastack(PyThreadState *tstate)
}
}

void
_Py_ClearFreeLists(_PyFreeListState *state, int is_finalization)
{
// 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);
_PyDict_ClearFreeList(state, is_finalization);
_PyContext_ClearFreeList(state, is_finalization);
_PyAsyncGen_ClearFreeLists(state, is_finalization);
_PyObjectStackChunk_ClearFreeList(state, is_finalization);
}

void
PyThreadState_Clear(PyThreadState *tstate)
{
Expand Down Expand Up @@ -1556,9 +1542,8 @@ PyThreadState_Clear(PyThreadState *tstate)
}
#ifdef Py_GIL_DISABLED
// Each thread should clear own freelists in free-threading builds.
_PyFreeListState *freelist_state = &((_PyThreadStateImpl*)tstate)->freelist_state;
_Py_ClearFreeLists(freelist_state, 1);
_PySlice_ClearCache(freelist_state);
_PyFreeListState *freelist_state = _PyFreeListState_GET();
_PyObject_ClearFreeLists(freelist_state, 1);
#endif

_PyThreadState_ClearMimallocHeaps(tstate);
Expand Down
Loading