Skip to content

Commit 3632600

Browse files
authored
Merge pull request #105 from kulisak12/regions-gc
Region GC
2 parents 665bc9d + d541323 commit 3632600

17 files changed

Lines changed: 898 additions & 200 deletions

Include/internal/pycore_cown.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,14 @@ typedef uint64_t _PyCown_ipid_t;
2222
typedef uint64_t _PyCown_thread_id_t;
2323

2424
//PyAPI_FUNC(PyObject*) _PyCown_New();
25-
// PyAPI_FUNC(int) _PyCown_SetValue(_PyCownObject* self, PyObject* value);
25+
PyAPI_FUNC(PyObject*) _PyCown_GetValue(_PyCownObject* self);
26+
PyAPI_FUNC(int) _PyCown_SetValue(_PyCownObject* self, PyObject* value);
2627
PyAPI_FUNC(_PyCown_ipid_t) _PyCown_ThisInterpreterId(void);
2728
PyAPI_FUNC(_PyCown_thread_id_t) _PyCown_ThisThreadId(void);
2829
PyAPI_FUNC(int) _PyCown_RegionOpen(_PyCownObject *self, _PyRegionObject* region, _PyCown_ipid_t ip);
29-
PyAPI_FUNC(int) _PyCown_AcquireGC(_PyCownObject *self, Py_region_t *region);
30+
PyAPI_FUNC(void) _PyCown_SetCollecting(_PyCownObject *self, int value);
3031
PyAPI_FUNC(int) _PyCown_SwitchFromGcToIp(_PyCownObject *self);
31-
PyAPI_FUNC(int) _PyCown_SwitchFromIpToGc(_PyCownObject *self, Py_region_t *contained_region);
32-
PyAPI_FUNC(int) _PyCown_ReleaseGC(_PyCownObject *self);
32+
PyAPI_FUNC(int) _PyCown_SwitchFromIpToGc(_PyCownObject *self);
3333

3434

3535
#ifdef __cplusplus

Include/internal/pycore_gc.h

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -118,20 +118,18 @@ static inline void _PyObject_GC_SET_SHARED(PyObject *op) {
118118
/* Bit 1 is set when the object is in generation which is GCed currently. */
119119
#define _PyGC_PREV_MASK_COLLECTING ((uintptr_t)2)
120120

121-
/* Bit 0 in _gc_next is the old space bit.
121+
/* Bit flags for _gc_next */
122+
/* Bit 0 is the old space bit.
123+
* It describes the generation space the object is in.
122124
* It is set as follows:
123125
* Young: gcstate->visited_space
124126
* old[0]: 0
125127
* old[1]: 1
126128
* permanent: 0
127-
*
128-
* During a collection all objects handled should have the bit set to
129-
* gcstate->visited_space, as objects are moved from the young gen
130-
* and the increment into old[gcstate->visited_space].
131-
* When object are moved from the pending space, old[gcstate->visited_space^1]
132-
* into the increment, the old space bit is flipped.
133129
*/
134-
#define _PyGC_NEXT_MASK_OLD_SPACE_1 1
130+
#define _PyGC_NEXT_MASK_OLD_SPACE_1 ((uintptr_t)1)
131+
/* Bit 1 is set when the object is in the unreachable list. */
132+
#define _PyGC_NEXT_MASK_UNREACHABLE ((uintptr_t)2)
135133

136134
#define _PyGC_PREV_SHIFT 2
137135
#define _PyGC_PREV_MASK (((uintptr_t) -1) << _PyGC_PREV_SHIFT)
@@ -340,6 +338,9 @@ extern void _PyGC_InitState(struct _gc_runtime_state *);
340338

341339
extern Py_ssize_t _PyGC_Collect(PyThreadState *tstate, int generation, _PyGC_Reason reason);
342340
extern void _PyGC_CollectNoFail(PyThreadState *tstate);
341+
extern Py_ssize_t _PyGC_CollectRegion(PyThreadState *tstate, PyObject *region, _PyGC_Reason reason);
342+
extern void _PyGC_IncreaseRegionBudget(PyThreadState *tstate);
343+
extern bool _PyGC_CanRunRegionGC(PyThreadState *tstate);
343344

344345
/* Freeze objects tracked by the GC and ignore them in future collections. */
345346
extern void _PyGC_Freeze(PyInterpreterState *interp);

Include/internal/pycore_global_objects_fini_generated.h

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_global_strings.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -712,6 +712,7 @@ struct _Py_global_strings {
712712
STRUCT_FOR_ID(readonly)
713713
STRUCT_FOR_ID(real)
714714
STRUCT_FOR_ID(reducer_override)
715+
STRUCT_FOR_ID(region)
715716
STRUCT_FOR_ID(registry)
716717
STRUCT_FOR_ID(rel_tol)
717718
STRUCT_FOR_ID(release)

Include/internal/pycore_interp_structs.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,9 @@ struct _gc_runtime_state {
227227
int visited_space;
228228
int phase;
229229

230+
/* How many objects in regions can be collected within this cycle */
231+
Py_ssize_t region_budget;
232+
230233
#ifdef Py_GIL_DISABLED
231234
/* This is the number of objects that survived the last full
232235
collection. It approximates the number of long lived objects

Include/internal/pycore_region.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,9 @@ typedef struct _Py_region_data {
9898
*/
9999
PyGC_Head gc_list;
100100

101+
/* List of unreachable objects in the region, saved to be deleted later. */
102+
PyGC_Head unreachable;
103+
101104
#ifdef Py_OWNERSHIP_INVARIANT
102105
_Py_ownership_invariant_region_data invariant_data;
103106
#endif
@@ -133,6 +136,7 @@ static inline Py_region_t __PyRegion_Get(PyObject *obj, int follow_pending) {
133136

134137
PyAPI_FUNC(int) _PyRegion_New(_PyRegionObject *bridge);
135138
PyAPI_FUNC(int) _PyRegion_Dissolve(Py_region_t region);
139+
PyAPI_FUNC(void) _PyRegion_IncRc(Py_region_t region);
136140
PyAPI_FUNC(void) _PyRegion_DecRc(Py_region_t region);
137141

138142
PyAPI_FUNC(Py_ssize_t) _PyRegion_GetLrc(Py_region_t region);

Include/internal/pycore_regionobject.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ struct _PyRegionObject {
2222
/** The name of the region or NULL */
2323
PyObject *name;
2424
PyObject *dict;
25+
/* A link in a list of regions to be garbage collected. */
26+
struct _PyRegionObject *next;
2527
};
2628
#define _PyRegionObject_CAST(op) _Py_CAST(_PyRegionObject*, op)
2729

@@ -30,4 +32,4 @@ PyAPI_DATA(PyTypeObject) _PyRegion_Type;
3032
#ifdef __cplusplus
3133
}
3234
#endif
33-
#endif /* !Py_INTERNAL_REGIONOBJECT_H */
35+
#endif /* !Py_INTERNAL_REGIONOBJECT_H */

Include/internal/pycore_runtime_init_generated.h

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_unicodeobject_generated.h

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

InternalDocs/garbage_collector.md

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -199,22 +199,22 @@ unreachable:
199199

200200
```pycon
201201
>>> import gc
202-
>>>
202+
>>>
203203
>>> class Link:
204204
... def __init__(self, next_link=None):
205205
... self.next_link = next_link
206-
...
206+
...
207207
>>> link_3 = Link()
208208
>>> link_2 = Link(link_3)
209209
>>> link_1 = Link(link_2)
210210
>>> link_3.next_link = link_1
211211
>>> A = link_1
212212
>>> del link_1, link_2, link_3
213-
>>>
213+
>>>
214214
>>> link_4 = Link()
215215
>>> link_4.next_link = link_4
216216
>>> del link_4
217-
>>>
217+
>>>
218218
>>> # Collect the unreachable Link object (and its .__dict__ dict).
219219
>>> gc.collect()
220220
2
@@ -278,7 +278,7 @@ state in the previous image and after examining the objects referred to by `link
278278
the GC knows that `link_3` is reachable after all, so it is moved back to the
279279
original list and its `gc_ref` field is set to 1 so that if the GC visits it again,
280280
it will know that it's reachable. To avoid visiting an object twice, the GC marks all
281-
objects that have already been visited once (by unsetting the `PREV_MASK_COLLECTING`
281+
objects that have already been visited once (by unsetting the `_PyGC_PREV_MASK_COLLECTING`
282282
flag) so that if an object that has already been processed is referenced by some other
283283
object, the GC does not process it twice.
284284

@@ -465,11 +465,11 @@ specifically in a generation by calling `gc.collect(generation=NUM)`.
465465
>>> # Create a reference cycle.
466466
>>> x = MyObj()
467467
>>> x.self = x
468-
>>>
468+
>>>
469469
>>> # Initially the object is in the young generation.
470470
>>> gc.get_objects(generation=0)
471471
[..., <__main__.MyObj object at 0x7fbcc12a3400>, ...]
472-
>>>
472+
>>>
473473
>>> # After a collection of the youngest generation the object
474474
>>> # moves to the old generation.
475475
>>> gc.collect(generation=0)
@@ -725,21 +725,27 @@ of `PyGC_Head` discussed in the `Memory layout and object structure`_ section:
725725

726726
- The `_gc_prev` field is normally used as the "previous" pointer to maintain the
727727
doubly linked list but its lowest two bits are used to keep the flags
728-
`PREV_MASK_COLLECTING` and `_PyGC_PREV_MASK_FINALIZED`. Between collections,
728+
`_PyGC_PREV_MASK_COLLECTING` and `_PyGC_PREV_MASK_FINALIZED`. Between collections,
729729
the only flag that can be present is `_PyGC_PREV_MASK_FINALIZED` that indicates
730730
if an object has been already finalized. During collections `_gc_prev` is
731731
temporarily used for storing a copy of the reference count (`gc_ref`), in
732732
addition to two flags, and the GC linked list becomes a singly linked list until
733733
`_gc_prev` is restored.
734734

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

744750
Optimization: delayed untracking containers
745751
===========================================

0 commit comments

Comments
 (0)