@@ -776,28 +776,40 @@ handle_legacy_finalizers(PyGC_Head *finalizers, PyGC_Head *old)
776776 return 0 ;
777777}
778778
779+ /* Run first-time finalizers (if any) on all the objects in collectable.
780+ * Note that this may remove some (or even all) of the objects from the
781+ * list, due to refcounts falling to 0.
782+ */
779783static void
780- finalize_garbage (PyGC_Head * collectable , PyGC_Head * old )
784+ finalize_garbage (PyGC_Head * collectable )
781785{
782786 destructor finalize ;
783- PyGC_Head * gc = collectable -> gc .gc_next ;
787+ PyGC_Head seen ;
788+
789+ /* While we're going through the loop, `finalize(op)` may cause op, or
790+ * other objects, to be reclaimed via refcounts falling to zero. So
791+ * there's little we can rely on about the structure of the input
792+ * `collectable` list across iterations. For safety, we always take the
793+ * first object in that list and move it to a temporary `seen` list.
794+ * If objects vanish from the `collectable` and `seen` lists we don't
795+ * care.
796+ */
797+ gc_list_init (& seen );
784798
785- for (; gc != collectable ; gc = gc -> gc .gc_next ) {
799+ while (!gc_list_is_empty (collectable )) {
800+ PyGC_Head * gc = collectable -> gc .gc_next ;
786801 PyObject * op = FROM_GC (gc );
787-
802+ gc_list_move ( gc , & seen );
788803 if (!_PyGCHead_FINALIZED (gc ) &&
789- PyType_HasFeature (Py_TYPE (op ), Py_TPFLAGS_HAVE_FINALIZE ) &&
790- (finalize = Py_TYPE (op )-> tp_finalize ) != NULL ) {
804+ PyType_HasFeature (Py_TYPE (op ), Py_TPFLAGS_HAVE_FINALIZE ) &&
805+ (finalize = Py_TYPE (op )-> tp_finalize ) != NULL ) {
791806 _PyGCHead_SET_FINALIZED (gc , 1 );
792807 Py_INCREF (op );
793808 finalize (op );
794- if (Py_REFCNT (op ) == 1 ) {
795- /* op will be destroyed */
796- gc = gc -> gc .gc_prev ;
797- }
798809 Py_DECREF (op );
799810 }
800811 }
812+ gc_list_merge (& seen , collectable );
801813}
802814
803815/* Walk the collectable list and check that they are really unreachable
@@ -1006,7 +1018,7 @@ collect(int generation, Py_ssize_t *n_collected, Py_ssize_t *n_uncollectable,
10061018 m += handle_weakrefs (& unreachable , old );
10071019
10081020 /* Call tp_finalize on objects which have one. */
1009- finalize_garbage (& unreachable , old );
1021+ finalize_garbage (& unreachable );
10101022
10111023 if (check_garbage (& unreachable )) {
10121024 revive_garbage (& unreachable );
0 commit comments