cpython/Include/internal/pycore_freelist.h
Sam Gross 5716cc3529
gh-100240: Use a consistent implementation for freelists (#121934)
This combines and updates our freelist handling to use a consistent
implementation. Objects in the freelist are linked together using the
first word of memory block.

If configured with freelists disabled, these operations are essentially
no-ops.
2024-07-22 12:08:27 -04:00

118 lines
3.2 KiB
C

#ifndef Py_INTERNAL_FREELIST_H
#define Py_INTERNAL_FREELIST_H
#ifdef __cplusplus
extern "C" {
#endif
#ifndef Py_BUILD_CORE
# error "this header requires Py_BUILD_CORE define"
#endif
#include "pycore_freelist_state.h" // struct _Py_freelists
#include "pycore_object.h" // _PyObject_IS_GC
#include "pycore_pystate.h" // _PyThreadState_GET
#include "pycore_code.h" // OBJECT_STAT_INC
static inline struct _Py_freelists *
_Py_freelists_GET(void)
{
PyThreadState *tstate = _PyThreadState_GET();
#ifdef Py_DEBUG
_Py_EnsureTstateNotNULL(tstate);
#endif
#ifdef Py_GIL_DISABLED
return &((_PyThreadStateImpl*)tstate)->freelists;
#else
return &tstate->interp->object_state.freelists;
#endif
}
#ifndef WITH_FREELISTS
#define _Py_FREELIST_FREE(NAME, op, freefunc) freefunc(op)
#define _Py_FREELIST_PUSH(NAME, op, limit) (0)
#define _Py_FREELIST_POP(TYPE, NAME) (NULL)
#define _Py_FREELIST_POP_MEM(NAME) (NULL)
#define _Py_FREELIST_SIZE(NAME) (0)
#else
// Pushes `op` to the freelist, calls `freefunc` if the freelist is full
#define _Py_FREELIST_FREE(NAME, op, freefunc) \
_PyFreeList_Free(&_Py_freelists_GET()->NAME, _PyObject_CAST(op), \
Py_ ## NAME ## _MAXFREELIST, freefunc)
// Pushes `op` to the freelist, returns 1 if successful, 0 if the freelist is full
#define _Py_FREELIST_PUSH(NAME, op, limit) \
_PyFreeList_Push(&_Py_freelists_GET()->NAME, _PyObject_CAST(op), limit)
// Pops a PyObject from the freelist, returns NULL if the freelist is empty.
#define _Py_FREELIST_POP(TYPE, NAME) \
_Py_CAST(TYPE*, _PyFreeList_Pop(&_Py_freelists_GET()->NAME))
// Pops a non-PyObject data structure from the freelist, returns NULL if the
// freelist is empty.
#define _Py_FREELIST_POP_MEM(NAME) \
_PyFreeList_PopMem(&_Py_freelists_GET()->NAME)
#define _Py_FREELIST_SIZE(NAME) (int)((_Py_freelists_GET()->NAME).size)
static inline int
_PyFreeList_Push(struct _Py_freelist *fl, void *obj, Py_ssize_t maxsize)
{
if (fl->size < maxsize && fl->size >= 0) {
*(void **)obj = fl->freelist;
fl->freelist = obj;
fl->size++;
OBJECT_STAT_INC(to_freelist);
return 1;
}
return 0;
}
static inline void
_PyFreeList_Free(struct _Py_freelist *fl, void *obj, Py_ssize_t maxsize,
freefunc dofree)
{
if (!_PyFreeList_Push(fl, obj, maxsize)) {
dofree(obj);
}
}
static inline void *
_PyFreeList_PopNoStats(struct _Py_freelist *fl)
{
void *obj = fl->freelist;
if (obj != NULL) {
assert(fl->size > 0);
fl->freelist = *(void **)obj;
fl->size--;
}
return obj;
}
static inline PyObject *
_PyFreeList_Pop(struct _Py_freelist *fl)
{
PyObject *op = _PyFreeList_PopNoStats(fl);
if (op != NULL) {
OBJECT_STAT_INC(from_freelist);
_Py_NewReference(op);
}
return op;
}
static inline void *
_PyFreeList_PopMem(struct _Py_freelist *fl)
{
void *op = _PyFreeList_PopNoStats(fl);
if (op != NULL) {
OBJECT_STAT_INC(from_freelist);
}
return op;
}
#endif
extern void _PyObject_ClearFreeLists(struct _Py_freelists *freelists, int is_finalization);
#ifdef __cplusplus
}
#endif
#endif /* !Py_INTERNAL_FREELIST_H */