Skip to content
Open
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
8 changes: 4 additions & 4 deletions Include/internal/pycore_cown.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@ typedef uint64_t _PyCown_ipid_t;
typedef uint64_t _PyCown_thread_id_t;

//PyAPI_FUNC(PyObject*) _PyCown_New();
// PyAPI_FUNC(int) _PyCown_SetValue(_PyCownObject* self, PyObject* value);
PyAPI_FUNC(PyObject*) _PyCown_GetValue(_PyCownObject* self);
PyAPI_FUNC(int) _PyCown_SetValue(_PyCownObject* self, PyObject* value);
PyAPI_FUNC(_PyCown_ipid_t) _PyCown_ThisInterpreterId(void);
PyAPI_FUNC(_PyCown_thread_id_t) _PyCown_ThisThreadId(void);
PyAPI_FUNC(int) _PyCown_RegionOpen(_PyCownObject *self, _PyRegionObject* region, _PyCown_ipid_t ip);
PyAPI_FUNC(int) _PyCown_AcquireGC(_PyCownObject *self, Py_region_t *region);
PyAPI_FUNC(void) _PyCown_SetCollecting(_PyCownObject *self, int value);
PyAPI_FUNC(int) _PyCown_SwitchFromGcToIp(_PyCownObject *self);
PyAPI_FUNC(int) _PyCown_SwitchFromIpToGc(_PyCownObject *self, Py_region_t *contained_region);
PyAPI_FUNC(int) _PyCown_ReleaseGC(_PyCownObject *self);
PyAPI_FUNC(int) _PyCown_SwitchFromIpToGc(_PyCownObject *self);


#ifdef __cplusplus
Expand Down
17 changes: 9 additions & 8 deletions Include/internal/pycore_gc.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,20 +118,18 @@ 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)

/* Bit 0 in _gc_next is the old space bit.
/* Bit flags for _gc_next */
/* Bit 0 is the old space bit.
* It describes the generation space the object is in.
* It is set as follows:
* Young: gcstate->visited_space
* old[0]: 0
* old[1]: 1
* permanent: 0
*
* During a collection all objects handled should have the bit set to
* gcstate->visited_space, as objects are moved from the young gen
* and the increment into old[gcstate->visited_space].
* When object are moved from the pending space, old[gcstate->visited_space^1]
* into the increment, the old space bit is flipped.
*/
#define _PyGC_NEXT_MASK_OLD_SPACE_1 1
#define _PyGC_NEXT_MASK_OLD_SPACE_1 ((uintptr_t)1)
/* Bit 1 is set when the object is in the unreachable list. */
#define _PyGC_NEXT_MASK_UNREACHABLE ((uintptr_t)2)

#define _PyGC_PREV_SHIFT 2
#define _PyGC_PREV_MASK (((uintptr_t) -1) << _PyGC_PREV_SHIFT)
Expand Down Expand Up @@ -340,6 +338,9 @@ 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 Py_ssize_t _PyGC_CollectRegion(PyThreadState *tstate, PyObject *region, _PyGC_Reason reason);
extern void _PyGC_IncreaseRegionBudget(PyThreadState *tstate);
extern bool _PyGC_CanRunRegionGC(PyThreadState *tstate);

/* Freeze objects tracked by the GC and ignore them in future collections. */
extern void _PyGC_Freeze(PyInterpreterState *interp);
Expand Down
1 change: 1 addition & 0 deletions Include/internal/pycore_global_objects_fini_generated.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Include/internal/pycore_global_strings.h
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,7 @@ struct _Py_global_strings {
STRUCT_FOR_ID(readonly)
STRUCT_FOR_ID(real)
STRUCT_FOR_ID(reducer_override)
STRUCT_FOR_ID(region)
STRUCT_FOR_ID(registry)
STRUCT_FOR_ID(rel_tol)
STRUCT_FOR_ID(release)
Expand Down
3 changes: 3 additions & 0 deletions Include/internal/pycore_interp_structs.h
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,9 @@ struct _gc_runtime_state {
int visited_space;
int phase;

/* How many objects in regions can be collected within this cycle */
Py_ssize_t region_budget;

#ifdef Py_GIL_DISABLED
/* This is the number of objects that survived the last full
collection. It approximates the number of long lived objects
Expand Down
4 changes: 4 additions & 0 deletions Include/internal/pycore_region.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ typedef struct _Py_region_data {
*/
PyGC_Head gc_list;

/* List of unreachable objects in the region, saved to be deleted later. */
PyGC_Head unreachable;

#ifdef Py_OWNERSHIP_INVARIANT
_Py_ownership_invariant_region_data invariant_data;
#endif
Expand Down Expand Up @@ -133,6 +136,7 @@ static inline Py_region_t __PyRegion_Get(PyObject *obj, int follow_pending) {

PyAPI_FUNC(int) _PyRegion_New(_PyRegionObject *bridge);
PyAPI_FUNC(int) _PyRegion_Dissolve(Py_region_t region);
PyAPI_FUNC(void) _PyRegion_IncRc(Py_region_t region);
PyAPI_FUNC(void) _PyRegion_DecRc(Py_region_t region);

PyAPI_FUNC(Py_ssize_t) _PyRegion_GetLrc(Py_region_t region);
Expand Down
4 changes: 3 additions & 1 deletion Include/internal/pycore_regionobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ struct _PyRegionObject {
/** The name of the region or NULL */
PyObject *name;
PyObject *dict;
/* A link in a list of regions to be garbage collected. */
struct _PyRegionObject *next;
};
#define _PyRegionObject_CAST(op) _Py_CAST(_PyRegionObject*, op)

Expand All @@ -30,4 +32,4 @@ PyAPI_DATA(PyTypeObject) _PyRegion_Type;
#ifdef __cplusplus
}
#endif
#endif /* !Py_INTERNAL_REGIONOBJECT_H */
#endif /* !Py_INTERNAL_REGIONOBJECT_H */
1 change: 1 addition & 0 deletions Include/internal/pycore_runtime_init_generated.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Include/internal/pycore_unicodeobject_generated.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 18 additions & 12 deletions InternalDocs/garbage_collector.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,22 +199,22 @@ unreachable:

```pycon
>>> import gc
>>>
>>>
>>> class Link:
... def __init__(self, next_link=None):
... self.next_link = next_link
...
...
>>> link_3 = Link()
>>> link_2 = Link(link_3)
>>> link_1 = Link(link_2)
>>> link_3.next_link = link_1
>>> A = link_1
>>> del link_1, link_2, link_3
>>>
>>>
>>> link_4 = Link()
>>> link_4.next_link = link_4
>>> del link_4
>>>
>>>
>>> # Collect the unreachable Link object (and its .__dict__ dict).
>>> gc.collect()
2
Expand Down Expand Up @@ -278,7 +278,7 @@ state in the previous image and after examining the objects referred to by `link
the GC knows that `link_3` is reachable after all, so it is moved back to the
original list and its `gc_ref` field is set to 1 so that if the GC visits it again,
it will know that it's reachable. To avoid visiting an object twice, the GC marks all
objects that have already been visited once (by unsetting the `PREV_MASK_COLLECTING`
objects that have already been visited once (by unsetting the `_PyGC_PREV_MASK_COLLECTING`
flag) so that if an object that has already been processed is referenced by some other
object, the GC does not process it twice.

Expand Down Expand Up @@ -465,11 +465,11 @@ specifically in a generation by calling `gc.collect(generation=NUM)`.
>>> # Create a reference cycle.
>>> x = MyObj()
>>> x.self = x
>>>
>>>
>>> # Initially the object is in the young generation.
>>> gc.get_objects(generation=0)
[..., <__main__.MyObj object at 0x7fbcc12a3400>, ...]
>>>
>>>
>>> # After a collection of the youngest generation the object
>>> # moves to the old generation.
>>> gc.collect(generation=0)
Expand Down Expand Up @@ -725,21 +725,27 @@ 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,
`_PyGC_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
`_gc_prev` is restored.

- The `_gc_next` field is used as the "next" pointer to maintain the doubly linked
list but during collection its lowest bit is used to keep the
`NEXT_MASK_UNREACHABLE` flag that indicates if an object is tentatively
- The `_gc_next` field is normally used as the "next" pointer to maintain the
doubly linked list but its lowest two bits are used to keep the flags
`_PyGC_NEXT_MASK_OLD_SPACE_1` and `_PyGC_NEXT_MASK_UNREACHABLE`.
During collection, the `_PyGC_NEXT_MASK_UNREACHABLE` flag indicates if an object is tentatively
unreachable during the cycle detection algorithm. This is a drawback to using only
doubly linked lists to implement partitions: while most needed operations are
constant-time, there is no efficient way to determine which partition an object is
currently in. Instead, when that's needed, ad hoc tricks (like the
`NEXT_MASK_UNREACHABLE` flag) are employed.
`_PyGC_NEXT_MASK_UNREACHABLE` flag) are employed.
The `_PyGC_NEXT_MASK_OLD_SPACE_1` flag
indicates whether the object belongs to the pending space or the
visited space. The objects in the pending space are yet to be processed
during future incremental collections. Which space is which is determined
by gcstate->visited_space.

Optimization: delayed untracking containers
===========================================
Expand Down
Loading
Loading