Skip to content

Commit f8745e0

Browse files
Split unreachable to types and objects while deduce
1 parent ac394bc commit f8745e0

File tree

1 file changed

+45
-36
lines changed

1 file changed

+45
-36
lines changed

Python/gc.c

Lines changed: 45 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -665,11 +665,13 @@ visit_reachable(PyObject *op, void *arg)
665665
* So we can not gc_list_* functions for unreachable until we remove the flag.
666666
*/
667667
static void
668-
move_unreachable(PyGC_Head *young, PyGC_Head *unreachable)
668+
move_unreachable(PyGC_Head *young, PyGC_Head *unreachable, PyGC_Head *unreachable_types)
669669
{
670670
// previous elem in the young list, used for restore gc_prev.
671671
PyGC_Head *prev = young;
672672
PyGC_Head *gc = GC_NEXT(young);
673+
PyGC_Head *to;
674+
PyObject *op;
673675

674676
/* Invariants: all objects "to the left" of us in young are reachable
675677
* (directly or indirectly) from outside the young list as it was at entry.
@@ -720,18 +722,26 @@ move_unreachable(PyGC_Head *young, PyGC_Head *unreachable)
720722
// No need to gc->next->prev = prev because it is single linked.
721723
prev->_gc_next = gc->_gc_next;
722724

725+
op = FROM_GC(gc);
726+
if (unreachable_types != NULL && PyType_Check(op)) {
727+
to = unreachable_types;
728+
}
729+
else {
730+
to = unreachable;
731+
}
732+
723733
// We can't use gc_list_append() here because we use
724734
// NEXT_MASK_UNREACHABLE here.
725-
PyGC_Head *last = GC_PREV(unreachable);
735+
PyGC_Head *last = GC_PREV(to);
726736
// NOTE: Since all objects in unreachable set has
727737
// NEXT_MASK_UNREACHABLE flag, we set it unconditionally.
728738
// But this may pollute the unreachable list head's 'next' pointer
729739
// too. That's semantically senseless but expedient here - the
730740
// damage is repaired when this function ends.
731741
last->_gc_next = flags | (uintptr_t)gc;
732742
_PyGCHead_SET_PREV(gc, last);
733-
gc->_gc_next = flags | (uintptr_t)unreachable;
734-
unreachable->_gc_prev = (uintptr_t)gc;
743+
gc->_gc_next = flags | (uintptr_t)to;
744+
to->_gc_prev = (uintptr_t)gc;
735745
}
736746
gc = _PyGCHead_NEXT(prev);
737747
}
@@ -740,6 +750,9 @@ move_unreachable(PyGC_Head *young, PyGC_Head *unreachable)
740750
young->_gc_next &= _PyGC_PREV_MASK;
741751
// don't let the pollution of the list head's next pointer leak
742752
unreachable->_gc_next &= _PyGC_PREV_MASK;
753+
if (unreachable_types != NULL) {
754+
unreachable_types->_gc_next &= _PyGC_PREV_MASK;
755+
}
743756
}
744757

745758
/* In theory, all tuples should be younger than the
@@ -858,21 +871,6 @@ move_legacy_finalizer_reachable(PyGC_Head *finalizers)
858871
}
859872
}
860873

861-
/* Move types from unreachable set to prevent clearing of type's subclasses */
862-
static void
863-
move_types_from_unreachable(PyGC_Head *unreachable, PyGC_Head *to)
864-
{
865-
PyGC_Head *gc, *next;
866-
for(gc = GC_NEXT(unreachable); gc != unreachable; gc = next) {
867-
PyObject *op = FROM_GC(gc);
868-
next = GC_NEXT(gc);
869-
870-
if (PyType_Check(op)) {
871-
gc_list_move(gc, to);
872-
}
873-
}
874-
}
875-
876874
/* Clear all weakrefs to unreachable objects, and if such a weakref has a
877875
* callback, invoke it if necessary. Note that it's possible for such
878876
* weakrefs to be outside the unreachable set -- indeed, those are precisely
@@ -1184,6 +1182,8 @@ delete_garbage(PyThreadState *tstate, GCState *gcstate,
11841182
them to the "unreachable" list. This step also needs to move back to "base" all
11851183
objects that were initially marked as unreachable but are referred transitively
11861184
by the reachable objects (the ones with strictly positive reference count).
1185+
4. Split unreachable objects and unreachable types to prevent clearing types
1186+
before instances.
11871187
11881188
Contracts:
11891189
@@ -1198,7 +1198,8 @@ flag is cleared (for example, by using 'clear_unreachable_mask' function or
11981198
by a call to 'move_legacy_finalizers'), the 'unreachable' list is not a normal
11991199
list and we can not use most gc_list_* functions for it. */
12001200
static inline void
1201-
deduce_unreachable(PyGC_Head *base, PyGC_Head *unreachable) {
1201+
deduce_unreachable(PyGC_Head *base, PyGC_Head *unreachable,
1202+
PyGC_Head* unreachable_types) {
12021203
validate_list(base, collecting_clear_unreachable_clear);
12031204
/* Using ob_refcnt and gc_refs, calculate which objects in the
12041205
* container set are reachable from outside the set (i.e., have a
@@ -1242,10 +1243,19 @@ deduce_unreachable(PyGC_Head *base, PyGC_Head *unreachable) {
12421243
* objects will remain unreachable, so it would be more efficient to move
12431244
* the reachable objects instead. But this is a one-time cost, probably not
12441245
* worth complicating the code to speed just a little.
1246+
*
1247+
* Note on types: All types in the unreachable set should be handled after
1248+
* the instances of those types are finalized. Otherwise, when we clear
1249+
* the weak references, the subclasses list will also be cleared, and
1250+
* the type's cache will not be properly invalidated from
1251+
* within the __del__ method.
12451252
*/
1246-
move_unreachable(base, unreachable); // gc_prev is pointer again
1253+
move_unreachable(base, unreachable, unreachable_types); // gc_prev is pointer again
12471254
validate_list(base, collecting_clear_unreachable_clear);
12481255
validate_list(unreachable, collecting_set_unreachable_set);
1256+
if (unreachable_types != NULL) {
1257+
validate_list(unreachable_types, collecting_set_unreachable_set);
1258+
}
12491259
}
12501260

12511261
/* Handle objects that may have resurrected after a call to 'finalize_garbage', moving
@@ -1273,7 +1283,7 @@ handle_resurrected_objects(PyGC_Head *unreachable, PyGC_Head* still_unreachable,
12731283
// have the PREV_MARK_COLLECTING set, but the objects are going to be
12741284
// removed so we can skip the expense of clearing the flag.
12751285
PyGC_Head* resurrected = unreachable;
1276-
deduce_unreachable(resurrected, still_unreachable);
1286+
deduce_unreachable(resurrected, still_unreachable, NULL);
12771287
clear_unreachable_mask(still_unreachable);
12781288

12791289
// Move the resurrected objects to the old generation for future collection.
@@ -1713,15 +1723,16 @@ gc_collect_region(PyThreadState *tstate,
17131723
{
17141724
PyGC_Head unreachable; /* non-problematic unreachable trash */
17151725
PyGC_Head finalizers; /* objects with, & reachable from, __del__ */
1716-
PyGC_Head types; /* unreachable types */
1726+
PyGC_Head unreachable_types; /* unreachable types */
17171727
PyGC_Head *gc; /* initialize to prevent a compiler warning */
17181728
GCState *gcstate = &tstate->interp->gc;
17191729

17201730
assert(gcstate->garbage != NULL);
17211731
assert(!_PyErr_Occurred(tstate));
17221732

17231733
gc_list_init(&unreachable);
1724-
deduce_unreachable(from, &unreachable);
1734+
gc_list_init(&unreachable_types);
1735+
deduce_unreachable(from, &unreachable, &unreachable_types);
17251736
validate_consistent_old_space(from);
17261737
untrack_tuples(from);
17271738
validate_consistent_old_space(to);
@@ -1738,29 +1749,26 @@ gc_collect_region(PyThreadState *tstate,
17381749
// NEXT_MASK_UNREACHABLE is cleared here.
17391750
// After move_legacy_finalizers(), unreachable is normal list.
17401751
move_legacy_finalizers(&unreachable, &finalizers);
1752+
move_legacy_finalizers(&unreachable_types, &finalizers);
17411753
/* finalizers contains the unreachable objects with a legacy finalizer;
17421754
* unreachable objects reachable *from* those are also uncollectable,
17431755
* and we move those into the finalizers list too.
17441756
*/
17451757
move_legacy_finalizer_reachable(&finalizers);
17461758
validate_list(&finalizers, collecting_clear_unreachable_clear);
17471759
validate_list(&unreachable, collecting_set_unreachable_clear);
1760+
validate_list(&unreachable_types, collecting_set_unreachable_clear);
17481761
/* Print debugging information. */
17491762
if (gcstate->debug & _PyGC_DEBUG_COLLECTABLE) {
17501763
for (gc = GC_NEXT(&unreachable); gc != &unreachable; gc = GC_NEXT(gc)) {
17511764
debug_cycle("collectable", FROM_GC(gc));
17521765
}
1766+
gc = GC_NEXT(&unreachable_types);
1767+
for (; gc != &unreachable_types; gc = GC_NEXT(gc)) {
1768+
debug_cycle("collectable", FROM_GC(gc));
1769+
}
17531770
}
17541771

1755-
/* All types in the unreachable set should be handled after the
1756-
* instances of those types are finalized. Otherwise, when we clear
1757-
* the weak references, the subclasses list will also be cleared, and
1758-
* the type's cache will not be properly invalidated from
1759-
* within the __del__ method.
1760-
*/
1761-
gc_list_init(&types);
1762-
move_types_from_unreachable(&unreachable, &types);
1763-
17641772
/* Clear weakrefs and invoke callbacks as necessary. */
17651773
stats->collected += handle_weakrefs(&unreachable, to);
17661774
gc_list_validate_space(to, gcstate->visited_space);
@@ -1771,17 +1779,18 @@ gc_collect_region(PyThreadState *tstate,
17711779
finalize_garbage(tstate, &unreachable);
17721780

17731781
/* Clear weakrefs to types and invoke callbacks as necessary. */
1774-
stats->collected += handle_weakrefs(&types, to);
1782+
stats->collected += handle_weakrefs(&unreachable_types, to);
17751783
gc_list_validate_space(to, gcstate->visited_space);
17761784
validate_list(to, collecting_clear_unreachable_clear);
1785+
validate_list(&unreachable_types, collecting_set_unreachable_clear);
17771786

17781787
/* Call tp_finalize on types. */
1779-
finalize_garbage(tstate, &types);
1788+
finalize_garbage(tstate, &unreachable_types);
17801789

17811790
/* Merge types back to unreachable to properly process resurected
17821791
* objects and so on.
17831792
*/
1784-
gc_list_merge(&types, &unreachable);
1793+
gc_list_merge(&unreachable_types, &unreachable);
17851794

17861795
/* Handle any objects that may have resurrected after the call
17871796
* to 'finalize_garbage' and continue the collection with the

0 commit comments

Comments
 (0)