Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions Include/internal/pycore_gc.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,15 @@ static inline void _PyObject_GC_SET_SHARED(PyObject *op) {
/* Bit 1 is set when the object is in generation which is GCed currently. */
#define _PyGC_PREV_MASK_COLLECTING ((uintptr_t)2)

#define _PyGC_PREV_SHIFT 2
#if !defined(Py_GIL_DISABLED) && SIZEOF_VOID_P > 4
// Pointer alignment reserves bit 2 for permanent ownership, including queued weakrefs.
# define _PyGC_PREV_MASK_PERM ((uintptr_t)4)
# define _PyGC_PREV_SHIFT 3
#else
// 32-bit and free-threaded builds retain the existing two-bit header layout.
# define _PyGC_PREV_SHIFT 2
#endif
// The remaining high bits contain the predecessor address or gc_refs.
#define _PyGC_PREV_MASK (((uintptr_t) -1) << _PyGC_PREV_SHIFT)

/* set for debugging information */
Expand Down Expand Up @@ -151,7 +159,7 @@ static inline void _PyGCHead_SET_NEXT(PyGC_Head *gc, PyGC_Head *next) {
gc->_gc_next = (uintptr_t)next;
}

// Lowest two bits of _gc_prev is used for _PyGC_PREV_MASK_* flags.
// Low bits of _gc_prev store _PyGC_PREV_MASK_* flags.
static inline PyGC_Head* _PyGCHead_PREV(PyGC_Head *gc) {
uintptr_t prev = (gc->_gc_prev & _PyGC_PREV_MASK);
return (PyGC_Head*)prev;
Expand Down
2 changes: 1 addition & 1 deletion Include/internal/pycore_interp_structs.h
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ typedef struct {
_Py_ALIGNED_DEF(_PyObject_MIN_ALIGNMENT, uintptr_t) _gc_next;

// Tagged pointer to previous object in the list.
// Lowest two bits are used for flags documented later.
// Low bits store the flags documented in pycore_gc.h.
// Those bits are made available by the struct's minimum alignment.
uintptr_t _gc_prev;
} PyGC_Head;
Expand Down
12 changes: 12 additions & 0 deletions Include/internal/pycore_pystate.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,18 @@ _Py_IsMainInterpreter(PyInterpreterState *interp)
return (interp == _PyInterpreterState_Main());
}

/* Return whether tracked objects may belong to another interpreter lifetime.
*
* Result:
* - false before any subinterpreter reservation or runtime reinitialization.
* - true after either event, including across Py_Finalize()/Py_Initialize().
*
* Extension-owned objects may outlive the interpreter whose GC list held them,
* so callers must not assume every tracked object belongs to the current
* runtime when this returns true.
*/
PyAPI_FUNC(bool) _PyInterpreterState_MayHaveForeignGcObjects(void);

extern int _Py_IsMainInterpreterFinalizing(PyInterpreterState *interp);

// Export for _interpreters module.
Expand Down
14 changes: 12 additions & 2 deletions Include/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,19 @@ whose size is determined when the object is allocated.

/* PyObjects are given a minimum alignment so that the least significant bits
* of an object pointer become available for other purposes.
* This must be an integer literal with the value (1 << _PyGC_PREV_SHIFT), number of bytes.
* For exposed layouts, this must be an integer literal with the value (1 << _PyGC_PREV_SHIFT), in bytes.
* Opaque headers retain 4 for source compatibility.
*/
#define _PyObject_MIN_ALIGNMENT 4
// ABI3T test headers may poison Py_GIL_DISABLED.
#ifndef _Py_OPAQUE_PYOBJECT
# if !defined(Py_GIL_DISABLED) && SIZEOF_VOID_P > 4
# define _PyObject_MIN_ALIGNMENT 8
# else
# define _PyObject_MIN_ALIGNMENT 4
# endif
#else
# define _PyObject_MIN_ALIGNMENT 4
#endif

/* Nothing is actually declared to be a PyObject, but every pointer to
* a Python object can be cast to a PyObject*. This is inheritance built
Expand Down
14 changes: 9 additions & 5 deletions InternalDocs/garbage_collector.md
Original file line number Diff line number Diff line change
Expand Up @@ -660,11 +660,15 @@ of `PyGC_Head` discussed in the `Memory layout and object structure`_ section:

- The `_gc_prev` field is normally used as the "previous" pointer to maintain the
doubly linked list but its lowest two bits are used to keep the flags
`PREV_MASK_COLLECTING` and `_PyGC_PREV_MASK_FINALIZED`. Between collections,
the only flag that can be present is `_PyGC_PREV_MASK_FINALIZED` that indicates
if an object has been already finalized. During collections `_gc_prev` is
temporarily used for storing a copy of the reference count (`gc_ref`), in
addition to two flags, and the GC linked list becomes a singly linked list until
`PREV_MASK_COLLECTING` and `_PyGC_PREV_MASK_FINALIZED`. On GIL builds where `SIZEOF_VOID_P > 4`,
bit 2 is also used for `_PyGC_PREV_MASK_PERM` to record permanent-generation
ownership, including weakrefs temporarily queued for callbacks. Unfreeze clears
this flag on objects in the permanent generation. Surviving queued weakrefs
have it cleared before moving to the old generation. Between collections,
`_PyGC_PREV_MASK_FINALIZED` indicates if an object has been already finalized,
and `_PyGC_PREV_MASK_PERM` can also remain set. During collections `_gc_prev`
is temporarily used for storing a copy of the reference count (`gc_ref`), in
addition to these flags, and the GC linked list becomes a singly linked list until
`_gc_prev` is restored.

- The `_gc_next` field is used as the "next" pointer to maintain the doubly linked
Expand Down
Loading
Loading