@@ -301,8 +301,18 @@ gc_list_size(PyGC_Head *list)
301301 return n ;
302302}
303303
304+ /* Walk the list and mark all objects as non-collecting */
305+ static inline void
306+ gc_list_clear_collecting (PyGC_Head * collectable )
307+ {
308+ PyGC_Head * gc ;
309+ for (gc = GC_NEXT (collectable ); gc != collectable ; gc = GC_NEXT (gc )) {
310+ gc_clear_collecting (gc );
311+ }
312+ }
313+
304314/* Append objects in a GC list to a Python list.
305- * Return 0 if all OK, < 0 if error (out of memory for list).
315+ * Return 0 if all OK, < 0 if error (out of memory for list)
306316 */
307317static int
308318append_objects (PyObject * py_list , PyGC_Head * gc_list )
@@ -613,6 +623,22 @@ move_legacy_finalizers(PyGC_Head *unreachable, PyGC_Head *finalizers)
613623 }
614624}
615625
626+ static inline void
627+ clear_unreachable_mask (PyGC_Head * unreachable )
628+ {
629+ /* Check that the list head does not have the unreachable bit set */
630+ assert (((uintptr_t )unreachable & NEXT_MASK_UNREACHABLE ) == 0 );
631+
632+ PyGC_Head * gc , * next ;
633+ unreachable -> _gc_next &= ~NEXT_MASK_UNREACHABLE ;
634+ for (gc = GC_NEXT (unreachable ); gc != unreachable ; gc = next ) {
635+ _PyObject_ASSERT ((PyObject * )FROM_GC (gc ), gc -> _gc_next & NEXT_MASK_UNREACHABLE );
636+ gc -> _gc_next &= ~NEXT_MASK_UNREACHABLE ;
637+ next = (PyGC_Head * )gc -> _gc_next ;
638+ }
639+ validate_list (unreachable , PREV_MASK_COLLECTING );
640+ }
641+
616642/* A traversal callback for move_legacy_finalizer_reachable. */
617643static int
618644visit_move (PyObject * op , PyGC_Head * tolist )
@@ -891,36 +917,6 @@ finalize_garbage(PyGC_Head *collectable)
891917 gc_list_merge (& seen , collectable );
892918}
893919
894- /* Walk the collectable list and check that they are really unreachable
895- from the outside (some objects could have been resurrected by a
896- finalizer). */
897- static int
898- check_garbage (PyGC_Head * collectable )
899- {
900- int ret = 0 ;
901- PyGC_Head * gc ;
902- for (gc = GC_NEXT (collectable ); gc != collectable ; gc = GC_NEXT (gc )) {
903- // Use gc_refs and break gc_prev again.
904- gc_set_refs (gc , Py_REFCNT (FROM_GC (gc )));
905- _PyObject_ASSERT (FROM_GC (gc ), gc_get_refs (gc ) != 0 );
906- }
907- subtract_refs (collectable );
908- PyGC_Head * prev = collectable ;
909- for (gc = GC_NEXT (collectable ); gc != collectable ; gc = GC_NEXT (gc )) {
910- _PyObject_ASSERT_WITH_MSG (FROM_GC (gc ),
911- gc_get_refs (gc ) >= 0 ,
912- "refcount is too small" );
913- if (gc_get_refs (gc ) != 0 ) {
914- ret = -1 ;
915- }
916- // Restore gc_prev here.
917- _PyGCHead_SET_PREV (gc , prev );
918- gc_clear_collecting (gc );
919- prev = gc ;
920- }
921- return ret ;
922- }
923-
924920/* Break reference cycles by clearing the containers involved. This is
925921 * tricky business as the lists can be changing and we don't know which
926922 * objects may be freed. It is possible I screwed something up here.
@@ -958,6 +954,7 @@ delete_garbage(struct _gc_runtime_state *state,
958954 }
959955 if (GC_NEXT (collectable ) == gc ) {
960956 /* object is still alive, move it, it may die later */
957+ gc_clear_collecting (gc );
961958 gc_list_move (gc , old );
962959 }
963960 }
@@ -1003,6 +1000,87 @@ show_stats_each_generations(struct _gc_runtime_state *state)
10031000 buf , gc_list_size (& state -> permanent_generation .head ));
10041001}
10051002
1003+ /* Deduce wich objects among "base" are unreachable from outside the list
1004+ and move them to 'unreachable'. The process consist in the following steps:
1005+
1006+ 1. Copy all reference counts to a different field (gc_prev is used to hold
1007+ this copy to save memory).
1008+ 2. Traverse all objects in "base" and visit all referred objects using
1009+ "tp_traverse" and for every visited object, substract 1 to the reference
1010+ count (the one that we copied in the previous step). After this step, all
1011+ objects that can be reached directly from outside must have strictly positive
1012+ reference count, while all unreachable objects must have a count of exactly 0.
1013+ 3. Indentify all unreachable objects (the ones with 0 reference count) and move
1014+ them to the "unreachable" list. This step also needs to move back to "base" all
1015+ objects that were initially marked as unreachable but are referred transitively
1016+ by the reachable objects (the ones with strictly positive reference count).
1017+
1018+ Contracts:
1019+
1020+ * The "base" has to be a valid list with no mask set.
1021+
1022+ * The "unreachable" list must be uninitialized (this function calls
1023+ gc_list_init over 'unreachable').
1024+
1025+ IMPORTANT: This function leaves 'unreachable' with the NEXT_MASK_UNREACHABLE
1026+ flag set but it does not clear it to skip unnecessary iteration. Before the
1027+ flag is cleared (for example, by using 'clear_unreachable_mask' function or
1028+ by a call to 'move_legacy_finalizers'), the 'unreachable' list is not a normal
1029+ list and we can not use most gc_list_* functions for it. */
1030+ static inline void
1031+ deduce_unreachable (PyGC_Head * base , PyGC_Head * unreachable ) {
1032+ validate_list (base , 0 );
1033+ /* Using ob_refcnt and gc_refs, calculate which objects in the
1034+ * container set are reachable from outside the set (i.e., have a
1035+ * refcount greater than 0 when all the references within the
1036+ * set are taken into account).
1037+ */
1038+ update_refs (base ); // gc_prev is used for gc_refs
1039+ subtract_refs (base );
1040+
1041+ /* Leave everything reachable from outside base in base, and move
1042+ * everything else (in base) to unreachable.
1043+ * NOTE: This used to move the reachable objects into a reachable
1044+ * set instead. But most things usually turn out to be reachable,
1045+ * so it's more efficient to move the unreachable things.
1046+ */
1047+ gc_list_init (unreachable );
1048+ move_unreachable (base , unreachable ); // gc_prev is pointer again
1049+ validate_list (base , 0 );
1050+ }
1051+
1052+ /* Handle objects that may have resurrected after a call to 'finalize_garbage', moving
1053+ them to 'old_generation' and placing the rest on 'still_unreachable'.
1054+
1055+ Contracts:
1056+ * After this function 'unreachable' must not be used anymore and 'still_unreachable'
1057+ will contain the objects that did not resurrect.
1058+
1059+ * The "still_unreachable" list must be uninitialized (this function calls
1060+ gc_list_init over 'still_unreachable').
1061+
1062+ IMPORTANT: After a call to this function, the 'still_unreachable' set will have the
1063+ PREV_MARK_COLLECTING set, but the objects in this set are going to be removed so
1064+ we can skip the expense of clearing the flag to avoid extra iteration. */
1065+ static inline void
1066+ handle_resurrected_objects (PyGC_Head * unreachable , PyGC_Head * still_unreachable ,
1067+ PyGC_Head * old_generation )
1068+ {
1069+ // Remove the PREV_MASK_COLLECTING from unreachable
1070+ // to prepare it for a new call to 'deduce_unreachable'
1071+ gc_list_clear_collecting (unreachable );
1072+
1073+ // After the call to deduce_unreachable, the 'still_unreachable' set will
1074+ // have the PREV_MARK_COLLECTING set, but the objects are going to be
1075+ // removed so we can skip the expense of clearing the flag.
1076+ PyGC_Head * resurrected = unreachable ;
1077+ deduce_unreachable (resurrected , still_unreachable );
1078+ clear_unreachable_mask (still_unreachable );
1079+
1080+ // Move the resurrected objects to the old generation for future collection.
1081+ gc_list_merge (resurrected , old_generation );
1082+ }
1083+
10061084/* This is the main function. Read this to understand how the
10071085 * collection process works. */
10081086static Py_ssize_t
@@ -1045,26 +1123,9 @@ collect(struct _gc_runtime_state *state, int generation,
10451123 old = GEN_HEAD (state , generation + 1 );
10461124 else
10471125 old = young ;
1048-
1049- validate_list (young , 0 );
10501126 validate_list (old , 0 );
1051- /* Using ob_refcnt and gc_refs, calculate which objects in the
1052- * container set are reachable from outside the set (i.e., have a
1053- * refcount greater than 0 when all the references within the
1054- * set are taken into account).
1055- */
1056- update_refs (young ); // gc_prev is used for gc_refs
1057- subtract_refs (young );
10581127
1059- /* Leave everything reachable from outside young in young, and move
1060- * everything else (in young) to unreachable.
1061- * NOTE: This used to move the reachable objects into a reachable
1062- * set instead. But most things usually turn out to be reachable,
1063- * so it's more efficient to move the unreachable things.
1064- */
1065- gc_list_init (& unreachable );
1066- move_unreachable (young , & unreachable ); // gc_prev is pointer again
1067- validate_list (young , 0 );
1128+ deduce_unreachable (young , & unreachable );
10681129
10691130 untrack_tuples (young );
10701131 /* Move reachable objects to next generation. */
@@ -1114,17 +1175,18 @@ collect(struct _gc_runtime_state *state, int generation,
11141175 /* Call tp_finalize on objects which have one. */
11151176 finalize_garbage (& unreachable );
11161177
1117- if (check_garbage (& unreachable )) { // clear PREV_MASK_COLLECTING here
1118- gc_list_merge (& unreachable , old );
1119- }
1120- else {
1121- /* Call tp_clear on objects in the unreachable set. This will cause
1122- * the reference cycles to be broken. It may also cause some objects
1123- * in finalizers to be freed.
1124- */
1125- m += gc_list_size (& unreachable );
1126- delete_garbage (state , & unreachable , old );
1127- }
1178+ /* Handle any objects that may have resurrected after the call
1179+ * to 'finalize_garbage' and continue the collection with the
1180+ * objects that are still unreachable */
1181+ PyGC_Head final_unreachable ;
1182+ handle_resurrected_objects (& unreachable , & final_unreachable , old );
1183+
1184+ /* Call tp_clear on objects in the final_unreachable set. This will cause
1185+ * the reference cycles to be broken. It may also cause some objects
1186+ * in finalizers to be freed.
1187+ */
1188+ m += gc_list_size (& final_unreachable );
1189+ delete_garbage (state , & final_unreachable , old );
11281190
11291191 /* Collect statistics on uncollectable objects found and print
11301192 * debugging information. */
0 commit comments