@@ -308,17 +308,18 @@ disable_deferred_refcounting(PyObject *op)
308308 // should also be disabled when we turn off deferred refcounting.
309309 _PyObject_DisablePerThreadRefcounting (op );
310310 }
311-
312- // Generators and frame objects may contain deferred references to other
313- // objects. If the pointed-to objects are part of cyclic trash, we may
314- // have disabled deferred refcounting on them and need to ensure that we
315- // use strong references, in case the generator or frame object is
316- // resurrected by a finalizer.
317- if (PyGen_CheckExact (op ) || PyCoro_CheckExact (op ) || PyAsyncGen_CheckExact (op )) {
318- frame_disable_deferred_refcounting (& ((PyGenObject * )op )-> gi_iframe );
319- }
320- else if (PyFrame_Check (op )) {
321- frame_disable_deferred_refcounting (((PyFrameObject * )op )-> f_frame );
311+ if (_PyObject_GC_IS_TRACKED (op )) {
312+ // Generators and frame objects may contain deferred references to other
313+ // objects. If the pointed-to objects are part of cyclic trash, we may
314+ // have disabled deferred refcounting on them and need to ensure that we
315+ // use strong references, in case the generator or frame object is
316+ // resurrected by a finalizer.
317+ if (PyGen_CheckExact (op ) || PyCoro_CheckExact (op ) || PyAsyncGen_CheckExact (op )) {
318+ frame_disable_deferred_refcounting (& ((PyGenObject * )op )-> gi_iframe );
319+ }
320+ else if (PyFrame_Check (op )) {
321+ frame_disable_deferred_refcounting (((PyFrameObject * )op )-> f_frame );
322+ }
322323 }
323324}
324325
@@ -1240,19 +1241,30 @@ scan_heap_visitor(const mi_heap_t *heap, const mi_heap_area_t *area,
12401241 return true;
12411242 }
12421243
1243- if (state -> reason == _Py_GC_REASON_SHUTDOWN ) {
1244- // Disable deferred refcounting for reachable objects as well during
1245- // interpreter shutdown. This ensures that these objects are collected
1246- // immediately when their last reference is removed.
1247- disable_deferred_refcounting (op );
1248- }
1249-
12501244 // object is reachable, restore `ob_tid`; we're done with these objects
12511245 gc_restore_tid (op );
12521246 gc_clear_alive (op );
12531247 return true;
12541248}
12551249
1250+ // Disable deferred refcounting for reachable objects during interpreter
1251+ // shutdown. This ensures that these objects are collected immediately when
1252+ // their last reference is removed. This needs to consider both tracked and
1253+ // untracked GC objects, since either might have deferred refcounts enabled.
1254+ static bool
1255+ scan_heap_disable_deferred (const mi_heap_t * heap , const mi_heap_area_t * area ,
1256+ void * block , size_t block_size , void * args )
1257+ {
1258+ PyObject * op = op_from_block_all_gc (block , args );
1259+ if (op == NULL ) {
1260+ return true;
1261+ }
1262+ if (!_Py_IsImmortal (op )) {
1263+ disable_deferred_refcounting (op );
1264+ }
1265+ return true;
1266+ }
1267+
12561268static int
12571269move_legacy_finalizer_reachable (struct collection_state * state );
12581270
@@ -1487,6 +1499,10 @@ deduce_unreachable_heap(PyInterpreterState *interp,
14871499 // Restores ob_tid for reachable objects.
14881500 gc_visit_heaps (interp , & scan_heap_visitor , & state -> base );
14891501
1502+ if (state -> reason == _Py_GC_REASON_SHUTDOWN ) {
1503+ gc_visit_heaps (interp , & scan_heap_disable_deferred , & state -> base );
1504+ }
1505+
14901506 if (state -> legacy_finalizers .head ) {
14911507 // There may be objects reachable from legacy finalizers that are in
14921508 // the unreachable set. We need to mark them as reachable.
0 commit comments