Skip to content

Commit 5f6ab4c

Browse files
committed
wip: code cleanup, small bug fix
More code cleanup (better names, comments, simplify error handling). Fix bug in that "alive" bit must be checked in mark_alive_stack_push() to avoid visiting already visited objects.
1 parent b99f2df commit 5f6ab4c

File tree

2 files changed

+40
-38
lines changed

2 files changed

+40
-38
lines changed

Include/internal/pycore_gc.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ static inline PyObject* _Py_FROM_GC(PyGC_Head *gc) {
5151
# define _PyGC_BITS_FROZEN (1<<3)
5252
# define _PyGC_BITS_SHARED (1<<4)
5353
# define _PyGC_BITS_ALIVE (1<<5) // Reachable from a known root.
54-
# define _PyGC_BITS_DEFERRED (1<<7) // Use deferred reference counting
54+
# define _PyGC_BITS_DEFERRED (1<<6) // Use deferred reference counting
5555
#endif
5656

5757
#ifdef Py_GIL_DISABLED

Python/gc_free_threading.c

Lines changed: 39 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -155,13 +155,13 @@ gc_is_unreachable(PyObject *op)
155155
return gc_has_bit(op, _PyGC_BITS_UNREACHABLE);
156156
}
157157

158-
static void
158+
static inline void
159159
gc_set_unreachable(PyObject *op)
160160
{
161161
gc_set_bit(op, _PyGC_BITS_UNREACHABLE);
162162
}
163163

164-
static void
164+
static inline void
165165
gc_clear_unreachable(PyObject *op)
166166
{
167167
gc_clear_bit(op, _PyGC_BITS_UNREACHABLE);
@@ -174,14 +174,14 @@ gc_is_alive(PyObject *op)
174174
}
175175

176176
#ifdef GC_ENABLE_MARK_ALIVE
177-
static void
177+
static inline void
178178
gc_set_alive(PyObject *op)
179179
{
180180
gc_set_bit(op, _PyGC_BITS_ALIVE);
181181
}
182182
#endif
183183

184-
static void
184+
static inline void
185185
gc_clear_alive(PyObject *op)
186186
{
187187
gc_clear_bit(op, _PyGC_BITS_ALIVE);
@@ -448,22 +448,26 @@ gc_visit_thread_stacks(PyInterpreterState *interp)
448448
}
449449

450450
#ifdef GC_ENABLE_MARK_ALIVE
451-
static bool
452-
mark_alive_stack_push(_PyObjectStack *stack, PyObject *op)
451+
static int
452+
mark_alive_stack_push(PyObject *op, _PyObjectStack *stack)
453453
{
454454
if (op == NULL) {
455-
return true;
455+
return 0;
456+
}
457+
if (!_PyObject_GC_IS_TRACKED(op)) {
458+
return 0;
459+
}
460+
if (gc_is_alive(op)) {
461+
return 0; // already visited this object
456462
}
457-
assert(!gc_is_alive(op));
458463
gc_set_alive(op);
459-
//gc_maybe_merge_refcount(op);
460464
if (_PyObjectStack_Push(stack, op) < 0) {
461-
return false;
465+
_PyObjectStack_Clear(stack);
466+
return -1;
462467
}
463-
return true;
468+
return 0;
464469
}
465470

466-
467471
#ifdef GC_MARK_ALIVE_STACKS
468472
static bool
469473
gc_visit_stackref_mark_alive(_PyObjectStack *stack, _PyStackRef stackref)
@@ -472,11 +476,9 @@ gc_visit_stackref_mark_alive(_PyObjectStack *stack, _PyStackRef stackref)
472476
// Otherwise we might read into invalid memory due to non-deferred references
473477
// being dead already.
474478
if (PyStackRef_IsDeferred(stackref) && !PyStackRef_IsNull(stackref)) {
475-
PyObject *obj = PyStackRef_AsPyObjectBorrow(stackref);
476-
if (_PyObject_GC_IS_TRACKED(obj) && !gc_is_frozen(obj)) {
477-
if (!mark_alive_stack_push(stack, obj)) {
478-
return false;
479-
}
479+
PyObject *op = PyStackRef_AsPyObjectBorrow(stackref);
480+
if (mark_alive_stack_push(op, stack) < 0) {
481+
return false;
480482
}
481483
}
482484
return true;
@@ -836,16 +838,6 @@ static int
836838
move_legacy_finalizer_reachable(struct collection_state *state);
837839

838840
#ifdef GC_ENABLE_MARK_ALIVE
839-
static int
840-
visit_propagate_alive(PyObject *op, _PyObjectStack *stack)
841-
{
842-
if (_PyObject_GC_IS_TRACKED(op) && !gc_is_alive(op)) {
843-
gc_set_alive(op);
844-
return _PyObjectStack_Push(stack, op);
845-
}
846-
return 0;
847-
}
848-
849841
static int
850842
propagate_alive_bits(_PyObjectStack *stack)
851843
{
@@ -857,27 +849,37 @@ propagate_alive_bits(_PyObjectStack *stack)
857849
assert(_PyObject_GC_IS_TRACKED(op));
858850
assert(gc_is_alive(op));
859851
traverseproc traverse = Py_TYPE(op)->tp_traverse;
860-
if (traverse(op, (visitproc)&visit_propagate_alive, stack) < 0) {
861-
_PyObjectStack_Clear(stack);
852+
if (traverse(op, (visitproc)&mark_alive_stack_push, stack) < 0) {
862853
return -1;
863854
}
864855
}
865856
return 0;
866857
}
867858

859+
// Using tp_traverse, mark everything reachable from known root objects
860+
// (which must be non-garbage) as alive (_PyGC_BITS_ALIVE is set). In
861+
// most programs, this marks nearly all objects that are not actually
862+
// unreachable. Actually alive objects can be missed in this pass if
863+
// they are alive due to being referenced from an unknown root (e.g. an
864+
// extension module global), some tp_traverse methods are either missing
865+
// or not accurate, or objects that have been untracked (and objects
866+
// referenced by those). If gc.freeze() is used, this pass is disabled
867+
// since it is unlikely to help much. The next stages of cyclic GC will
868+
// ignore objects with the alive bit set.
869+
//
870+
// Returns -1 on failure (out of memory).
868871
static int
869-
mark_root_reachable(PyInterpreterState *interp,
870-
struct collection_state *state)
872+
mark_alive_from_roots(PyInterpreterState *interp,
873+
struct collection_state *state)
871874
{
872875
#ifdef GC_DEBUG
873-
// Check that all objects don't have ALIVE bits set
876+
// Check that all objects don't have alive bit set
874877
gc_visit_heaps(interp, &validate_alive_bits, &state->base);
875878
#endif
876879
_PyObjectStack stack = { NULL };
877880

878881
#define STACK_PUSH(op) \
879-
if (!mark_alive_stack_push(&stack, op)) { \
880-
_PyObjectStack_Clear(&stack); \
882+
if (mark_alive_stack_push(op, &stack) < 0) { \
881883
return -1; \
882884
}
883885
STACK_PUSH(interp->sysdict);
@@ -896,12 +898,12 @@ mark_root_reachable(PyInterpreterState *interp,
896898
#endif
897899
#ifdef GC_MARK_ALIVE_STACKS
898900
if (!gc_visit_thread_stacks_mark_alive(interp, &stack)) {
899-
_PyObjectStack_Clear(&stack);
900901
return -1;
901902
}
902903
#endif
903904
#undef STACK_PUSH
904905

906+
// Use tp_traverse to find everything reachable from roots.
905907
propagate_alive_bits(&stack);
906908

907909
return 0;
@@ -1472,7 +1474,7 @@ gc_collect_internal(PyInterpreterState *interp, struct collection_state *state,
14721474
if (!state->gcstate->freeze_active) {
14731475
// Mark objects reachable from known roots as "alive". These will
14741476
// be ignored for rest of the GC pass.
1475-
int err = mark_root_reachable(interp, state);
1477+
int err = mark_alive_from_roots(interp, state);
14761478
if (err < 0) {
14771479
_PyEval_StartTheWorld(interp);
14781480
PyErr_NoMemory();
@@ -1490,7 +1492,7 @@ gc_collect_internal(PyInterpreterState *interp, struct collection_state *state,
14901492
}
14911493

14921494
#ifdef GC_DEBUG
1493-
// At this point, no object should have ALIVE bit set
1495+
// At this point, no object should have the alive bit set
14941496
gc_visit_heaps(interp, &validate_alive_bits, &state->base);
14951497
#endif
14961498

0 commit comments

Comments
 (0)