| #ifndef Py_INTERNAL_GC_H |
| #define Py_INTERNAL_GC_H |
| #ifdef __cplusplus |
| extern "C" { |
| #endif |
|
|
| #ifndef Py_BUILD_CORE |
| # error "this header requires Py_BUILD_CORE define" |
| #endif |
|
|
| #include "pycore_freelist.h" |
|
|
| |
| typedef struct { |
| |
| |
| uintptr_t _gc_next; |
|
|
| |
| |
| uintptr_t _gc_prev; |
| } PyGC_Head; |
|
|
| #define _PyGC_Head_UNUSED PyGC_Head |
|
|
|
|
| |
| static inline PyGC_Head* _Py_AS_GC(PyObject *op) { |
| char *gc = ((char*)op) - sizeof(PyGC_Head); |
| return (PyGC_Head*)gc; |
| } |
|
|
| |
| static inline PyObject* _Py_FROM_GC(PyGC_Head *gc) { |
| char *op = ((char *)gc) + sizeof(PyGC_Head); |
| return (PyObject *)op; |
| } |
|
|
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| #ifdef Py_GIL_DISABLED |
| # define _PyGC_BITS_TRACKED (1) |
| # define _PyGC_BITS_FINALIZED (2) |
| # define _PyGC_BITS_UNREACHABLE (4) |
| # define _PyGC_BITS_FROZEN (8) |
| # define _PyGC_BITS_SHARED (16) |
| # define _PyGC_BITS_SHARED_INLINE (32) |
| # define _PyGC_BITS_DEFERRED (64) |
| #endif |
|
|
| #ifdef Py_GIL_DISABLED |
|
|
| static inline void |
| _PyObject_SET_GC_BITS(PyObject *op, uint8_t new_bits) |
| { |
| uint8_t bits = _Py_atomic_load_uint8_relaxed(&op->ob_gc_bits); |
| _Py_atomic_store_uint8_relaxed(&op->ob_gc_bits, bits | new_bits); |
| } |
|
|
| static inline int |
| _PyObject_HAS_GC_BITS(PyObject *op, uint8_t bits) |
| { |
| return (_Py_atomic_load_uint8_relaxed(&op->ob_gc_bits) & bits) != 0; |
| } |
|
|
| static inline void |
| _PyObject_CLEAR_GC_BITS(PyObject *op, uint8_t bits_to_clear) |
| { |
| uint8_t bits = _Py_atomic_load_uint8_relaxed(&op->ob_gc_bits); |
| _Py_atomic_store_uint8_relaxed(&op->ob_gc_bits, bits & ~bits_to_clear); |
| } |
|
|
| #endif |
|
|
| |
| static inline int _PyObject_GC_IS_TRACKED(PyObject *op) { |
| #ifdef Py_GIL_DISABLED |
| return _PyObject_HAS_GC_BITS(op, _PyGC_BITS_TRACKED); |
| #else |
| PyGC_Head *gc = _Py_AS_GC(op); |
| return (gc->_gc_next != 0); |
| #endif |
| } |
| #define _PyObject_GC_IS_TRACKED(op) _PyObject_GC_IS_TRACKED(_Py_CAST(PyObject*, op)) |
|
|
| |
| |
| static inline int _PyObject_GC_MAY_BE_TRACKED(PyObject *obj) { |
| if (!PyObject_IS_GC(obj)) { |
| return 0; |
| } |
| if (PyTuple_CheckExact(obj)) { |
| return _PyObject_GC_IS_TRACKED(obj); |
| } |
| return 1; |
| } |
|
|
| #ifdef Py_GIL_DISABLED |
|
|
| |
| |
| |
| |
| |
| |
| static inline int _PyObject_GC_IS_SHARED(PyObject *op) { |
| return _PyObject_HAS_GC_BITS(op, _PyGC_BITS_SHARED); |
| } |
| #define _PyObject_GC_IS_SHARED(op) _PyObject_GC_IS_SHARED(_Py_CAST(PyObject*, op)) |
|
|
| static inline void _PyObject_GC_SET_SHARED(PyObject *op) { |
| _PyObject_SET_GC_BITS(op, _PyGC_BITS_SHARED); |
| } |
| #define _PyObject_GC_SET_SHARED(op) _PyObject_GC_SET_SHARED(_Py_CAST(PyObject*, op)) |
|
|
| |
| |
| |
| |
| |
| static inline int _PyObject_GC_IS_SHARED_INLINE(PyObject *op) { |
| return _PyObject_HAS_GC_BITS(op, _PyGC_BITS_SHARED_INLINE); |
| } |
| #define _PyObject_GC_IS_SHARED_INLINE(op) \ |
| _PyObject_GC_IS_SHARED_INLINE(_Py_CAST(PyObject*, op)) |
|
|
| static inline void _PyObject_GC_SET_SHARED_INLINE(PyObject *op) { |
| _PyObject_SET_GC_BITS(op, _PyGC_BITS_SHARED_INLINE); |
| } |
| #define _PyObject_GC_SET_SHARED_INLINE(op) \ |
| _PyObject_GC_SET_SHARED_INLINE(_Py_CAST(PyObject*, op)) |
|
|
| #endif |
|
|
| |
| |
| #define _PyGC_PREV_MASK_FINALIZED (1) |
| |
| #define _PyGC_PREV_MASK_COLLECTING (2) |
| |
| #define _PyGC_PREV_SHIFT (2) |
| #define _PyGC_PREV_MASK (((uintptr_t) -1) << _PyGC_PREV_SHIFT) |
|
|
| |
| #define _PyGC_DEBUG_STATS (1<<0) |
| #define _PyGC_DEBUG_COLLECTABLE (1<<1) |
| #define _PyGC_DEBUG_UNCOLLECTABLE (1<<2) |
| #define _PyGC_DEBUG_SAVEALL (1<<5) |
| #define _PyGC_DEBUG_LEAK _PyGC_DEBUG_COLLECTABLE | \ |
| _PyGC_DEBUG_UNCOLLECTABLE | \ |
| _PyGC_DEBUG_SAVEALL |
|
|
| typedef enum { |
| |
| _Py_GC_REASON_HEAP, |
|
|
| |
| _Py_GC_REASON_SHUTDOWN, |
|
|
| |
| _Py_GC_REASON_MANUAL |
| } _PyGC_Reason; |
|
|
| |
| |
| static inline PyGC_Head* _PyGCHead_NEXT(PyGC_Head *gc) { |
| uintptr_t next = gc->_gc_next; |
| return (PyGC_Head*)next; |
| } |
| static inline void _PyGCHead_SET_NEXT(PyGC_Head *gc, PyGC_Head *next) { |
| gc->_gc_next = (uintptr_t)next; |
| } |
|
|
| |
| static inline PyGC_Head* _PyGCHead_PREV(PyGC_Head *gc) { |
| uintptr_t prev = (gc->_gc_prev & _PyGC_PREV_MASK); |
| return (PyGC_Head*)prev; |
| } |
| static inline void _PyGCHead_SET_PREV(PyGC_Head *gc, PyGC_Head *prev) { |
| uintptr_t uprev = (uintptr_t)prev; |
| assert((uprev & ~_PyGC_PREV_MASK) == 0); |
| gc->_gc_prev = ((gc->_gc_prev & ~_PyGC_PREV_MASK) | uprev); |
| } |
|
|
| static inline int _PyGC_FINALIZED(PyObject *op) { |
| #ifdef Py_GIL_DISABLED |
| return _PyObject_HAS_GC_BITS(op, _PyGC_BITS_FINALIZED); |
| #else |
| PyGC_Head *gc = _Py_AS_GC(op); |
| return ((gc->_gc_prev & _PyGC_PREV_MASK_FINALIZED) != 0); |
| #endif |
| } |
| static inline void _PyGC_SET_FINALIZED(PyObject *op) { |
| #ifdef Py_GIL_DISABLED |
| _PyObject_SET_GC_BITS(op, _PyGC_BITS_FINALIZED); |
| #else |
| PyGC_Head *gc = _Py_AS_GC(op); |
| gc->_gc_prev |= _PyGC_PREV_MASK_FINALIZED; |
| #endif |
| } |
| static inline void _PyGC_CLEAR_FINALIZED(PyObject *op) { |
| #ifdef Py_GIL_DISABLED |
| _PyObject_CLEAR_GC_BITS(op, _PyGC_BITS_FINALIZED); |
| #else |
| PyGC_Head *gc = _Py_AS_GC(op); |
| gc->_gc_prev &= ~_PyGC_PREV_MASK_FINALIZED; |
| #endif |
| } |
|
|
|
|
| |
|
|
| |
| |
| #define NUM_GENERATIONS 3 |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| struct gc_generation { |
| PyGC_Head head; |
| int threshold; |
| int count; |
| |
| }; |
|
|
| |
| struct gc_generation_stats { |
| |
| Py_ssize_t collections; |
| |
| Py_ssize_t collected; |
| |
| Py_ssize_t uncollectable; |
| }; |
|
|
| struct _gc_runtime_state { |
| |
| |
| PyObject *trash_delete_later; |
| |
| int trash_delete_nesting; |
|
|
| |
| int enabled; |
| int debug; |
| |
| struct gc_generation generations[NUM_GENERATIONS]; |
| PyGC_Head *generation0; |
| |
| struct gc_generation permanent_generation; |
| struct gc_generation_stats generation_stats[NUM_GENERATIONS]; |
| |
| int collecting; |
| |
| PyObject *garbage; |
| |
| PyObject *callbacks; |
|
|
| |
| |
| |
| |
| |
| |
| Py_ssize_t long_lived_total; |
| |
| |
| |
| Py_ssize_t long_lived_pending; |
|
|
| #ifdef Py_GIL_DISABLED |
| |
| |
| |
| |
| |
| |
| int immortalize; |
| #endif |
| }; |
|
|
| #ifdef Py_GIL_DISABLED |
| struct _gc_thread_state { |
| |
| Py_ssize_t alloc_count; |
| }; |
| #endif |
|
|
|
|
| extern void _PyGC_InitState(struct _gc_runtime_state *); |
|
|
| extern Py_ssize_t _PyGC_Collect(PyThreadState *tstate, int generation, |
| _PyGC_Reason reason); |
| extern void _PyGC_CollectNoFail(PyThreadState *tstate); |
|
|
| |
| extern void _PyGC_Freeze(PyInterpreterState *interp); |
| |
| extern void _PyGC_Unfreeze(PyInterpreterState *interp); |
| |
| extern Py_ssize_t _PyGC_GetFreezeCount(PyInterpreterState *interp); |
|
|
| extern PyObject *_PyGC_GetObjects(PyInterpreterState *interp, int generation); |
| extern PyObject *_PyGC_GetReferrers(PyInterpreterState *interp, PyObject *objs); |
|
|
| |
| extern void _PyGC_ClearAllFreeLists(PyInterpreterState *interp); |
| extern void _Py_ScheduleGC(PyThreadState *tstate); |
| extern void _Py_RunGC(PyThreadState *tstate); |
|
|
| #ifdef Py_GIL_DISABLED |
| |
| extern void _PyGC_ImmortalizeDeferredObjects(PyInterpreterState *interp); |
| #endif |
|
|
| #ifdef __cplusplus |
| } |
| #endif |
| #endif |
|
|