// should also be disabled when we turn off deferred refcounting.
_PyObject_DisablePerThreadRefcounting(op);
}
-
- // Generators and frame objects may contain deferred references to other
- // objects. If the pointed-to objects are part of cyclic trash, we may
- // have disabled deferred refcounting on them and need to ensure that we
- // use strong references, in case the generator or frame object is
- // resurrected by a finalizer.
- if (PyGen_CheckExact(op) || PyCoro_CheckExact(op) || PyAsyncGen_CheckExact(op)) {
- frame_disable_deferred_refcounting(&((PyGenObject *)op)->gi_iframe);
- }
- else if (PyFrame_Check(op)) {
- frame_disable_deferred_refcounting(((PyFrameObject *)op)->f_frame);
+ if (_PyObject_GC_IS_TRACKED(op)) {
+ // Generators and frame objects may contain deferred references to other
+ // objects. If the pointed-to objects are part of cyclic trash, we may
+ // have disabled deferred refcounting on them and need to ensure that we
+ // use strong references, in case the generator or frame object is
+ // resurrected by a finalizer.
+ if (PyGen_CheckExact(op) || PyCoro_CheckExact(op) || PyAsyncGen_CheckExact(op)) {
+ frame_disable_deferred_refcounting(&((PyGenObject *)op)->gi_iframe);
+ }
+ else if (PyFrame_Check(op)) {
+ frame_disable_deferred_refcounting(((PyFrameObject *)op)->f_frame);
+ }
}
}
return true;
}
- if (state->reason == _Py_GC_REASON_SHUTDOWN) {
- // Disable deferred refcounting for reachable objects as well during
- // interpreter shutdown. This ensures that these objects are collected
- // immediately when their last reference is removed.
- disable_deferred_refcounting(op);
- }
-
// object is reachable, restore `ob_tid`; we're done with these objects
gc_restore_tid(op);
gc_clear_alive(op);
return true;
}
+// Disable deferred refcounting for reachable objects during interpreter
+// shutdown. This ensures that these objects are collected immediately when
+// their last reference is removed. This needs to consider both tracked and
+// untracked GC objects, since either might have deferred refcounts enabled.
+static bool
+scan_heap_disable_deferred(const mi_heap_t *heap, const mi_heap_area_t *area,
+ void *block, size_t block_size, void *args)
+{
+ PyObject *op = op_from_block_all_gc(block, args);
+ if (op == NULL) {
+ return true;
+ }
+ if (!_Py_IsImmortal(op)) {
+ disable_deferred_refcounting(op);
+ }
+ return true;
+}
+
static int
move_legacy_finalizer_reachable(struct collection_state *state);
// Restores ob_tid for reachable objects.
gc_visit_heaps(interp, &scan_heap_visitor, &state->base);
+ if (state->reason == _Py_GC_REASON_SHUTDOWN) {
+ gc_visit_heaps(interp, &scan_heap_disable_deferred, &state->base);
+ }
+
if (state->legacy_finalizers.head) {
// There may be objects reachable from legacy finalizers that are in
// the unreachable set. We need to mark them as reachable.