55#include "Python.h"
66#include "pycore_ceval.h" // _Py_set_eval_breaker_bit()
77#include "pycore_dict.h" // _PyInlineValuesSize()
8+ #include "pycore_genobject.h" // _PyGen_FinalizerIsNoop()
89#include "pycore_initconfig.h" // _PyStatus_OK()
910#include "pycore_context.h"
1011#include "pycore_interp.h" // PyInterpreterState.gc
@@ -805,7 +806,8 @@ move_legacy_finalizer_reachable(PyGC_Head *finalizers)
805806 * Python-level code. See bpo-38006 as an example bug.
806807 */
807808static int
808- handle_weakref_callbacks (PyGC_Head * unreachable , PyGC_Head * old )
809+ handle_weakref_callbacks (PyGC_Head * unreachable , PyGC_Head * old ,
810+ bool * needs_resurrection_check )
809811{
810812 PyGC_Head * gc ;
811813 PyGC_Head wrcb_to_call ; /* weakrefs with callbacks to call */
@@ -906,6 +908,9 @@ handle_weakref_callbacks(PyGC_Head *unreachable, PyGC_Head *old)
906908 /* Invoke the callbacks we decided to honor. It's safe to invoke them
907909 * because they can't reference unreachable objects.
908910 */
911+ if (!* needs_resurrection_check ) {
912+ * needs_resurrection_check = !gc_list_is_empty (& wrcb_to_call );
913+ }
909914 while (! gc_list_is_empty (& wrcb_to_call )) {
910915 PyObject * temp ;
911916 PyObject * callback ;
@@ -1043,7 +1048,8 @@ handle_legacy_finalizers(PyThreadState *tstate,
10431048 * list, due to refcounts falling to 0.
10441049 */
10451050static void
1046- finalize_garbage (PyThreadState * tstate , PyGC_Head * collectable )
1051+ finalize_garbage (PyThreadState * tstate , PyGC_Head * collectable ,
1052+ bool * needs_resurrection_check )
10471053{
10481054 destructor finalize ;
10491055 PyGC_Head seen ;
@@ -1065,6 +1071,9 @@ finalize_garbage(PyThreadState *tstate, PyGC_Head *collectable)
10651071 if (!_PyGC_FINALIZED (op ) &&
10661072 (finalize = Py_TYPE (op )-> tp_finalize ) != NULL )
10671073 {
1074+ if (!_PyGen_FinalizerIsNoop (op )) {
1075+ * needs_resurrection_check = true;
1076+ }
10681077 _PyGC_SET_FINALIZED (op );
10691078 Py_INCREF (op );
10701079 finalize (op );
@@ -1550,26 +1559,39 @@ gc_collect_main(PyThreadState *tstate, int generation, _PyGC_Reason reason)
15501559 validate_list (& finalizers , collecting_clear_unreachable_clear );
15511560 validate_list (& unreachable , collecting_set_unreachable_clear );
15521561
1562+ bool needs_resurrection_check = false;
1563+
15531564 /* Print debugging information. */
15541565 if (gcstate -> debug & _PyGC_DEBUG_COLLECTABLE ) {
1566+ /* sys.stderr.write() can resurrect objects. */
1567+ needs_resurrection_check = true;
15551568 for (gc = GC_NEXT (& unreachable ); gc != & unreachable ; gc = GC_NEXT (gc )) {
15561569 debug_cycle ("collectable" , FROM_GC (gc ));
15571570 }
15581571 }
15591572
15601573 /* Clear weakrefs and invoke callbacks as necessary. */
1561- stats .collected += handle_weakref_callbacks (& unreachable , old );
1574+ stats .collected += handle_weakref_callbacks (& unreachable , old ,
1575+ & needs_resurrection_check );
15621576 validate_list (old , collecting_clear_unreachable_clear );
15631577 validate_list (& unreachable , collecting_set_unreachable_clear );
15641578
15651579 /* Call tp_finalize on objects which have one. */
1566- finalize_garbage (tstate , & unreachable );
1580+ finalize_garbage (tstate , & unreachable , & needs_resurrection_check );
15671581
15681582 /* Handle any objects that may have resurrected after the call
15691583 * to 'finalize_garbage' and continue the collection with the
15701584 * objects that are still unreachable */
15711585 PyGC_Head final_unreachable ;
1572- handle_resurrected_objects (& unreachable , & final_unreachable , old );
1586+ if (needs_resurrection_check ) {
1587+ handle_resurrected_objects (& unreachable , & final_unreachable , old );
1588+ }
1589+ else {
1590+ /* No Python code ran during finalization, so the original
1591+ * unreachable set is still valid. */
1592+ gc_list_init (& final_unreachable );
1593+ gc_list_merge (& unreachable , & final_unreachable );
1594+ }
15731595
15741596 /* Clear weakrefs to objects in the unreachable set. No Python-level
15751597 * code must be allowed to access those unreachable objects. During
0 commit comments