Skip to content

Commit 0882016

Browse files
committed
gh-135909: Assert incoming refcnt != 0 for the free threaded GC.
This is to catch out double deallocation bugs. Likely from a faulty .tp_dealloc allowing the GC to run before the object is untracked. This assert matches the one added to the GIL build, in 780c497. The call to validate_refcounts was moved up to start of the GC because queue_untracked_obj_decref() creates it own zero reference count garbage.
1 parent a4625d5 commit 0882016

File tree

1 file changed

+15
-7
lines changed

1 file changed

+15
-7
lines changed

Python/gc_free_threading.c

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1073,6 +1073,14 @@ validate_refcounts(const mi_heap_t *heap, const mi_heap_area_t *area,
10731073
return true;
10741074
}
10751075

1076+
// This assert mirrors the one in Python/gc.c:update_refs(). There must be
1077+
// no tracked objects with a reference count of 0 when the cyclic
1078+
// collector starts. If there is, then the collector will double dealloc
1079+
// the object. The likely cause for hitting this is a faulty .tp_dealloc.
1080+
// Also see the comment in `update_refs()`.
1081+
_PyObject_ASSERT_WITH_MSG(op, Py_REFCNT(op) > 0,
1082+
"tracked objects must have a reference count > 0");
1083+
10761084
_PyObject_ASSERT_WITH_MSG(op, !gc_is_unreachable(op),
10771085
"object should not be marked as unreachable yet");
10781086

@@ -1422,13 +1430,6 @@ static int
14221430
deduce_unreachable_heap(PyInterpreterState *interp,
14231431
struct collection_state *state)
14241432
{
1425-
1426-
#ifdef GC_DEBUG
1427-
// Check that all objects are marked as unreachable and that the computed
1428-
// reference count difference (stored in `ob_tid`) is non-negative.
1429-
gc_visit_heaps(interp, &validate_refcounts, &state->base);
1430-
#endif
1431-
14321433
// Identify objects that are directly reachable from outside the GC heap
14331434
// by computing the difference between the refcount and the number of
14341435
// incoming references.
@@ -2154,6 +2155,13 @@ gc_collect_internal(PyInterpreterState *interp, struct collection_state *state,
21542155
state->gcstate->old[i-1].count = 0;
21552156
}
21562157

2158+
#ifdef GC_DEBUG
2159+
// Before we start, check that the heap is in a good condition. There must
2160+
// be no objects with a zero reference count. And `ob_tid` must only have a
2161+
// thread if the refcount is unmerged.
2162+
gc_visit_heaps(interp, &validate_refcounts, &state->base);
2163+
#endif
2164+
21572165
_Py_FOR_EACH_TSTATE_BEGIN(interp, p) {
21582166
_PyThreadStateImpl *tstate = (_PyThreadStateImpl *)p;
21592167

0 commit comments

Comments
 (0)