@@ -756,7 +756,7 @@ move_legacy_finalizer_reachable(PyGC_Head *finalizers)
756756 }
757757}
758758
759- /* Handle weakref callbacks. Note that it's possible for such weakrefs to be
759+ /* Prepare weakref callbacks. Note that it's possible for such weakrefs to be
760760 * outside the unreachable set -- indeed, those are precisely the weakrefs
761761 * whose callbacks must be invoked. See gc_weakref.txt for overview & some
762762 * details.
@@ -794,17 +794,20 @@ move_legacy_finalizer_reachable(PyGC_Head *finalizers)
794794 * or missing `tp_traverse` methods. When that object goes away, the callback
795795 * for weakref can be executed and that could reveal unreachable objects to
796796 * Python-level code. See bpo-38006 as an example bug.
797+ *
798+ * Arguments:
799+ * - `unreachable`: normalized collecting list to inspect.
800+ * - `wrcb_to_call`: initialized list receiving weakrefs whose callbacks are safe to invoke.
801+ *
802+ * Return whether any finalizer is pending or a weakref callback is queued.
803+ * This function does not invoke callbacks.
797804 */
798- static int
799- handle_weakref_callbacks (PyGC_Head * unreachable , PyGC_Head * old ,
800- bool * needs_resurrection_check )
805+ Py_NO_INLINE static bool
806+ prepare_callbacks (PyGC_Head * unreachable , PyGC_Head * wrcb_to_call )
801807{
802808 PyGC_Head * gc ;
803- PyGC_Head wrcb_to_call ; /* weakrefs with callbacks to call */
804809 PyGC_Head * next ;
805- int num_freed = 0 ;
806-
807- gc_list_init (& wrcb_to_call );
810+ bool has_pending_finalizer = false;
808811
809812 /* Find all weakrefs with callbacks and move into `wrcb_to_call` if the
810813 * callback needs to be invoked. We make another pass over wrcb_to_call,
@@ -814,9 +817,13 @@ handle_weakref_callbacks(PyGC_Head *unreachable, PyGC_Head *old,
814817 PyWeakReference * * wrlist ;
815818
816819 PyObject * op = FROM_GC (gc );
820+ PyTypeObject * type = Py_TYPE (op );
821+ if (type -> tp_finalize != NULL && !has_pending_finalizer ) {
822+ has_pending_finalizer = !_PyGC_FINALIZED (op );
823+ }
817824 next = GC_NEXT (gc );
818825
819- if (! _PyType_SUPPORTS_WEAKREFS (Py_TYPE ( op ) )) {
826+ if (! _PyType_SUPPORTS_WEAKREFS (type )) {
820827 continue ;
821828 }
822829
@@ -891,21 +898,35 @@ handle_weakref_callbacks(PyGC_Head *unreachable, PyGC_Head *old,
891898 PyGC_Head * wrasgc = AS_GC ((PyObject * )wr );
892899 // wrasgc is reachable, but next isn't, so they can't be the same
893900 _PyObject_ASSERT ((PyObject * )wr , wrasgc != next );
894- gc_list_move (wrasgc , & wrcb_to_call );
901+ gc_list_move (wrasgc , wrcb_to_call );
895902 }
896903 }
897904
905+ return has_pending_finalizer || !gc_list_is_empty (wrcb_to_call );
906+ }
907+
908+ /* Invoke queued weakref callbacks and move surviving weakrefs to `old`.
909+ *
910+ * Arguments:
911+ * - `wrcb_to_call`: initialized queue to drain, with a temporary reference to each weakref.
912+ * - `old`: generation receiving weakrefs that survive callback invocation.
913+ *
914+ * Return the number of weakrefs reclaimed while callbacks ran.
915+ */
916+ static int
917+ invoke_weakref_callbacks (PyGC_Head * wrcb_to_call , PyGC_Head * old )
918+ {
919+ PyGC_Head * gc ;
920+ int num_freed = 0 ;
921+
898922 /* Invoke the callbacks we decided to honor. It's safe to invoke them
899923 * because they can't reference unreachable objects.
900924 */
901- if (!* needs_resurrection_check ) {
902- * needs_resurrection_check = !gc_list_is_empty (& wrcb_to_call );
903- }
904- while (! gc_list_is_empty (& wrcb_to_call )) {
925+ while (! gc_list_is_empty (wrcb_to_call )) {
905926 PyObject * temp ;
906927 PyObject * callback ;
907928
908- gc = (PyGC_Head * )wrcb_to_call . _gc_next ;
929+ gc = (PyGC_Head * )wrcb_to_call -> _gc_next ;
909930 PyObject * op = FROM_GC (gc );
910931 _PyObject_ASSERT (op , PyWeakref_Check (op ));
911932 PyWeakReference * wr = (PyWeakReference * )op ;
@@ -934,7 +955,7 @@ handle_weakref_callbacks(PyGC_Head *unreachable, PyGC_Head *old,
934955 * ours).
935956 */
936957 Py_DECREF (op );
937- if (wrcb_to_call . _gc_next == (uintptr_t )gc ) {
958+ if (wrcb_to_call -> _gc_next == (uintptr_t )gc ) {
938959 /* object is still alive -- move it */
939960 gc_list_move (gc , old );
940961 }
@@ -948,7 +969,7 @@ handle_weakref_callbacks(PyGC_Head *unreachable, PyGC_Head *old,
948969
949970/* Clear all weakrefs to unreachable objects. When this returns, no object in
950971 * `unreachable` is weakly referenced anymore. See the comments above
951- * handle_weakref_callbacks () for why these weakrefs need to be cleared.
972+ * prepare_callbacks () for why these weakrefs need to be cleared.
952973 */
953974static void
954975clear_weakrefs (PyGC_Head * unreachable )
@@ -964,7 +985,7 @@ clear_weakrefs(PyGC_Head *unreachable)
964985
965986 if (PyWeakref_Check (op )) {
966987 /* A weakref inside the unreachable set is always cleared. See
967- * the comments above handle_weakref_callbacks () for why these
988+ * the comments above prepare_callbacks () for why these
968989 * must be cleared.
969990 */
970991 _PyWeakref_ClearRef ((PyWeakReference * )op );
@@ -1036,13 +1057,14 @@ handle_legacy_finalizers(PyThreadState *tstate,
10361057/* Run first-time finalizers (if any) on all the objects in collectable.
10371058 * Note that this may remove some (or even all) of the objects from the
10381059 * list, due to refcounts falling to 0.
1060+ * Return whether a finalizer may have run Python.
10391061 */
1040- static void
1041- finalize_garbage (PyThreadState * tstate , PyGC_Head * collectable ,
1042- bool * needs_resurrection_check )
1062+ static bool
1063+ finalize_garbage (PyThreadState * tstate , PyGC_Head * collectable )
10431064{
10441065 destructor finalize ;
10451066 PyGC_Head seen ;
1067+ bool needs_resurrection_check = false;
10461068
10471069 /* While we're going through the loop, `finalize(op)` may cause op, or
10481070 * other objects, to be reclaimed via refcounts falling to zero. So
@@ -1062,7 +1084,7 @@ finalize_garbage(PyThreadState *tstate, PyGC_Head *collectable,
10621084 (finalize = Py_TYPE (op )-> tp_finalize ) != NULL )
10631085 {
10641086 if (!_PyGen_FinalizerIsNoop (op )) {
1065- * needs_resurrection_check = true;
1087+ needs_resurrection_check = true;
10661088 }
10671089 _PyGC_SET_FINALIZED (op );
10681090 Py_INCREF (op );
@@ -1072,6 +1094,7 @@ finalize_garbage(PyThreadState *tstate, PyGC_Head *collectable,
10721094 }
10731095 }
10741096 gc_list_merge (& seen , collectable );
1097+ return needs_resurrection_check ;
10751098}
10761099
10771100/* Break reference cycles by clearing the containers involved. This is
@@ -1253,6 +1276,43 @@ handle_resurrected_objects(PyGC_Head *unreachable, PyGC_Head* still_unreachable,
12531276}
12541277
12551278
1279+ /* Invoke weakref callbacks and finalizers, then handle possible resurrection.
1280+ *
1281+ * Arguments:
1282+ * - `tstate`: the thread state used when invoking finalizers.
1283+ * - `collectable`: normalized collecting list whose finalizers may run.
1284+ * - `still_unreachable`: distinct, uninitialized list head for remaining garbage.
1285+ * - `old`: generation receiving surviving weakrefs and resurrected objects.
1286+ * - `wrcb_to_call`: initialized, possibly empty list of callback-bearing weakrefs to drain.
1287+ * - `collected_count`: count incremented by weakrefs reclaimed while callbacks run.
1288+ * - `needs_resurrection_check`: whether earlier work may have run Python.
1289+ *
1290+ * Return whether resurrection analysis ran. If true, `collectable` must not be
1291+ * used again and `still_unreachable` contains the remaining garbage. Otherwise,
1292+ * `collectable` remains selected and `still_unreachable` is untouched.
1293+ */
1294+ Py_NO_INLINE static bool
1295+ handle_callbacks (PyThreadState * tstate , PyGC_Head * collectable ,
1296+ PyGC_Head * still_unreachable , PyGC_Head * old ,
1297+ PyGC_Head * wrcb_to_call , Py_ssize_t * collected_count ,
1298+ bool needs_resurrection_check )
1299+ {
1300+ bool weakref_callbacks_invoked = !gc_list_is_empty (wrcb_to_call );
1301+ * collected_count += invoke_weakref_callbacks (wrcb_to_call , old );
1302+ needs_resurrection_check |= weakref_callbacks_invoked ;
1303+ validate_list (old , collecting_clear_unreachable_clear );
1304+ validate_list (collectable , collecting_set_unreachable_clear );
1305+
1306+ // Weakref callbacks can make finalizers newly eligible after the initial
1307+ // scan, so we must recheck even when we previously found no pending finalizers.
1308+ needs_resurrection_check |= finalize_garbage (tstate , collectable );
1309+ if (needs_resurrection_check ) {
1310+ handle_resurrected_objects (collectable , still_unreachable , old );
1311+ }
1312+ return needs_resurrection_check ;
1313+ }
1314+
1315+
12561316/* Invoke progress callbacks to notify clients that garbage collection
12571317 * is starting or stopping
12581318 */
@@ -1563,43 +1623,36 @@ gc_collect_main(PyThreadState *tstate, int generation, _PyGC_Reason reason)
15631623 }
15641624 }
15651625
1566- /* Clear weakrefs and invoke callbacks as necessary. */
1567- stats .collected += handle_weakref_callbacks (& unreachable , old ,
1568- & needs_resurrection_check );
1569- validate_list (old , collecting_clear_unreachable_clear );
1570- validate_list (& unreachable , collecting_set_unreachable_clear );
1571-
1572- /* Call tp_finalize on objects which have one. */
1573- finalize_garbage (tstate , & unreachable , & needs_resurrection_check );
1626+ PyGC_Head wrcb_to_call ;
1627+ gc_list_init (& wrcb_to_call );
1628+ bool has_pending_callbacks = prepare_callbacks (& unreachable , & wrcb_to_call );
1629+ has_pending_callbacks |= needs_resurrection_check ;
15741630
1575- /* Handle any objects that may have resurrected after the call
1576- * to 'finalize_garbage' and continue the collection with the
1577- * objects that are still unreachable */
15781631 PyGC_Head final_unreachable ;
1579- if (needs_resurrection_check ) {
1580- handle_resurrected_objects (& unreachable , & final_unreachable , old );
1581- }
1582- else {
1583- /* No Python code ran during finalization, so the original
1584- * unreachable set is still valid. */
1585- gc_list_init (& final_unreachable );
1586- gc_list_merge (& unreachable , & final_unreachable );
1632+ PyGC_Head * collectable = & unreachable ;
1633+ if (has_pending_callbacks ) {
1634+ bool resurrection_checked = handle_callbacks (tstate , collectable , & final_unreachable , old , & wrcb_to_call , & stats .collected , needs_resurrection_check );
1635+ if (resurrection_checked ) {
1636+ collectable = & final_unreachable ;
1637+ }
15871638 }
1639+ validate_list (old , collecting_clear_unreachable_clear );
1640+ validate_list (collectable , collecting_set_unreachable_clear );
15881641
15891642 /* Clear weakrefs to objects in the unreachable set. No Python-level
15901643 * code must be allowed to access those unreachable objects. During
15911644 * delete_garbage(), finalizers outside the unreachable set might run
15921645 * and create new weakrefs. If those weakrefs were not cleared, they
15931646 * could reveal unreachable objects. Callbacks are not executed.
15941647 */
1595- clear_weakrefs (& final_unreachable );
1648+ clear_weakrefs (collectable );
15961649
1597- /* Call tp_clear on objects in the final_unreachable set. This will cause
1650+ /* Call tp_clear on objects in the collectable set. This will cause
15981651 * the reference cycles to be broken. It may also cause some objects
15991652 * in finalizers to be freed.
16001653 */
1601- stats .collected += gc_list_size (& final_unreachable );
1602- delete_garbage (tstate , gcstate , & final_unreachable , old );
1654+ stats .collected += gc_list_size (collectable );
1655+ delete_garbage (tstate , gcstate , collectable , old );
16031656
16041657 /* Collect statistics on uncollectable objects found and print
16051658 * debugging information. */
0 commit comments