@@ -663,12 +663,13 @@ visit_reachable(PyObject *op, void *arg)
663663 * But _gc_next in unreachable list has NEXT_MASK_UNREACHABLE flag.
664664 * So we can not gc_list_* functions for unreachable until we remove the flag.
665665 */
666- static void
666+ static Py_ssize_t
667667move_unreachable (PyGC_Head * young , PyGC_Head * unreachable )
668668{
669669 // previous elem in the young list, used for restore gc_prev.
670670 PyGC_Head * prev = young ;
671671 PyGC_Head * gc = GC_NEXT (young );
672+ Py_ssize_t visited = 0 ;
672673
673674 /* Invariants: all objects "to the left" of us in young are reachable
674675 * (directly or indirectly) from outside the young list as it was at entry.
@@ -683,6 +684,7 @@ move_unreachable(PyGC_Head *young, PyGC_Head *unreachable)
683684 /* Record which old space we are in, and set NEXT_MASK_UNREACHABLE bit for convenience */
684685 uintptr_t flags = NEXT_MASK_UNREACHABLE | (gc -> _gc_next & _PyGC_NEXT_MASK_OLD_SPACE_1 );
685686 while (gc != young ) {
687+ visited ++ ;
686688 if (gc_get_refs (gc )) {
687689 /* gc is definitely reachable from outside the
688690 * original 'young'. Mark it as such, and traverse
@@ -739,6 +741,7 @@ move_unreachable(PyGC_Head *young, PyGC_Head *unreachable)
739741 young -> _gc_next &= _PyGC_PREV_MASK ;
740742 // don't let the pollution of the list head's next pointer leak
741743 unreachable -> _gc_next &= _PyGC_PREV_MASK ;
744+ return visited ;
742745}
743746
744747/* In theory, all tuples should be younger than the
@@ -1240,7 +1243,7 @@ flag set but it does not clear it to skip unnecessary iteration. Before the
12401243flag is cleared (for example, by using 'clear_unreachable_mask' function or
12411244by a call to 'move_legacy_finalizers'), the 'unreachable' list is not a normal
12421245list and we can not use most gc_list_* functions for it. */
1243- static inline void
1246+ static inline Py_ssize_t
12441247deduce_unreachable (PyGC_Head * base , PyGC_Head * unreachable ) {
12451248 validate_list (base , collecting_clear_unreachable_clear );
12461249 /* Using ob_refcnt and gc_refs, calculate which objects in the
@@ -1286,9 +1289,10 @@ deduce_unreachable(PyGC_Head *base, PyGC_Head *unreachable) {
12861289 * the reachable objects instead. But this is a one-time cost, probably not
12871290 * worth complicating the code to speed just a little.
12881291 */
1289- move_unreachable (base , unreachable ); // gc_prev is pointer again
1292+ Py_ssize_t visited = move_unreachable (base , unreachable ); // gc_prev is pointer again
12901293 validate_list (base , collecting_clear_unreachable_clear );
12911294 validate_list (unreachable , collecting_set_unreachable_set );
1295+ return visited ;
12921296}
12931297
12941298/* Handle objects that may have resurrected after a call to 'finalize_garbage', moving
@@ -1316,7 +1320,7 @@ handle_resurrected_objects(PyGC_Head *unreachable, PyGC_Head* still_unreachable,
13161320 // have the PREV_MARK_COLLECTING set, but the objects are going to be
13171321 // removed so we can skip the expense of clearing the flag.
13181322 PyGC_Head * resurrected = unreachable ;
1319- deduce_unreachable (resurrected , still_unreachable );
1323+ ( void ) deduce_unreachable (resurrected , still_unreachable );
13201324 clear_unreachable_mask (still_unreachable );
13211325
13221326 // Move the resurrected objects to the old generation for future collection.
@@ -1364,6 +1368,7 @@ static void
13641368add_stats (GCState * gcstate , int gen , struct gc_collection_stats * stats )
13651369{
13661370 gcstate -> generation_stats [gen ].duration += stats -> duration ;
1371+ gcstate -> generation_stats [gen ].visited += stats -> visited ;
13671372 gcstate -> generation_stats [gen ].collected += stats -> collected ;
13681373 gcstate -> generation_stats [gen ].uncollectable += stats -> uncollectable ;
13691374 gcstate -> generation_stats [gen ].collections += 1 ;
@@ -1754,7 +1759,7 @@ gc_collect_region(PyThreadState *tstate,
17541759 assert (!_PyErr_Occurred (tstate ));
17551760
17561761 gc_list_init (& unreachable );
1757- deduce_unreachable (from , & unreachable );
1762+ stats -> visited += deduce_unreachable (from , & unreachable );
17581763 validate_consistent_old_space (from );
17591764 untrack_tuples (from );
17601765
@@ -1844,10 +1849,11 @@ do_gc_callback(GCState *gcstate, const char *phase,
18441849 assert (PyList_CheckExact (gcstate -> callbacks ));
18451850 PyObject * info = NULL ;
18461851 if (PyList_GET_SIZE (gcstate -> callbacks ) != 0 ) {
1847- info = Py_BuildValue ("{sisnsnsd }" ,
1852+ info = Py_BuildValue ("{sisnsnsnsd }" ,
18481853 "generation" , generation ,
18491854 "collected" , stats -> collected ,
18501855 "uncollectable" , stats -> uncollectable ,
1856+ "visited" , stats -> visited ,
18511857 "duration" , stats -> duration );
18521858 if (info == NULL ) {
18531859 PyErr_FormatUnraisable ("Exception ignored while invoking gc callbacks" );
0 commit comments