@@ -805,7 +805,8 @@ move_legacy_finalizer_reachable(PyGC_Head *finalizers)
805805 * Python-level code. See bpo-38006 as an example bug.
806806 */
807807static int
808- handle_weakref_callbacks (PyGC_Head * unreachable , PyGC_Head * old )
808+ handle_weakref_callbacks (PyGC_Head * unreachable , PyGC_Head * old ,
809+ bool * needs_resurrection_check )
809810{
810811 PyGC_Head * gc ;
811812 PyGC_Head wrcb_to_call ; /* weakrefs with callbacks to call */
@@ -917,6 +918,7 @@ handle_weakref_callbacks(PyGC_Head *unreachable, PyGC_Head *old)
917918 callback = wr -> wr_callback ;
918919 _PyObject_ASSERT (op , callback != NULL );
919920
921+ * needs_resurrection_check = true;
920922 /* copy-paste of weakrefobject.c's handle_callback() */
921923 temp = PyObject_CallOneArg (callback , (PyObject * )wr );
922924 if (temp == NULL ) {
@@ -1043,7 +1045,8 @@ handle_legacy_finalizers(PyThreadState *tstate,
10431045 * list, due to refcounts falling to 0.
10441046 */
10451047static void
1046- finalize_garbage (PyThreadState * tstate , PyGC_Head * collectable )
1048+ finalize_garbage (PyThreadState * tstate , PyGC_Head * collectable ,
1049+ bool * needs_resurrection_check )
10471050{
10481051 destructor finalize ;
10491052 PyGC_Head seen ;
@@ -1065,6 +1068,7 @@ finalize_garbage(PyThreadState *tstate, PyGC_Head *collectable)
10651068 if (!_PyGC_FINALIZED (op ) &&
10661069 (finalize = Py_TYPE (op )-> tp_finalize ) != NULL )
10671070 {
1071+ * needs_resurrection_check = true;
10681072 _PyGC_SET_FINALIZED (op );
10691073 Py_INCREF (op );
10701074 finalize (op );
@@ -1550,26 +1554,38 @@ gc_collect_main(PyThreadState *tstate, int generation, _PyGC_Reason reason)
15501554 validate_list (& finalizers , collecting_clear_unreachable_clear );
15511555 validate_list (& unreachable , collecting_set_unreachable_clear );
15521556
1557+ bool needs_resurrection_check = false;
1558+
15531559 /* Print debugging information. */
15541560 if (gcstate -> debug & _PyGC_DEBUG_COLLECTABLE ) {
1561+ /* sys.stderr.write() can resurrect objects. */
1562+ needs_resurrection_check = true;
15551563 for (gc = GC_NEXT (& unreachable ); gc != & unreachable ; gc = GC_NEXT (gc )) {
15561564 debug_cycle ("collectable" , FROM_GC (gc ));
15571565 }
15581566 }
15591567
15601568 /* Clear weakrefs and invoke callbacks as necessary. */
1561- stats .collected += handle_weakref_callbacks (& unreachable , old );
1569+ stats .collected += handle_weakref_callbacks (& unreachable , old ,
1570+ & needs_resurrection_check );
15621571 validate_list (old , collecting_clear_unreachable_clear );
15631572 validate_list (& unreachable , collecting_set_unreachable_clear );
15641573
15651574 /* Call tp_finalize on objects which have one. */
1566- finalize_garbage (tstate , & unreachable );
1575+ finalize_garbage (tstate , & unreachable , & needs_resurrection_check );
15671576
15681577 /* Handle any objects that may have resurrected after the call
15691578 * to 'finalize_garbage' and continue the collection with the
15701579 * objects that are still unreachable */
15711580 PyGC_Head final_unreachable ;
1572- handle_resurrected_objects (& unreachable , & final_unreachable , old );
1581+ if (needs_resurrection_check ) {
1582+ handle_resurrected_objects (& unreachable , & final_unreachable , old );
1583+ }
1584+ else {
1585+ /* No callbacks ran, so the original unreachable set is still valid. */
1586+ gc_list_init (& final_unreachable );
1587+ gc_list_merge (& unreachable , & final_unreachable );
1588+ }
15731589
15741590 /* Clear weakrefs to objects in the unreachable set. No Python-level
15751591 * code must be allowed to access those unreachable objects. During
0 commit comments