@@ -455,10 +455,20 @@ validate_consistent_old_space(PyGC_Head *head)
455455 assert (prev == GC_PREV (head ));
456456}
457457
458+ static void
459+ gc_list_validate_space (PyGC_Head * head , int space ) {
460+ PyGC_Head * gc = GC_NEXT (head );
461+ while (gc != head ) {
462+ assert (gc_old_space (gc ) == space );
463+ gc = GC_NEXT (gc );
464+ }
465+ }
466+
458467#else
459468#define validate_list (x , y ) do{}while(0)
460469#define validate_old (g ) do{}while(0)
461470#define validate_consistent_old_space (l ) do{}while(0)
471+ #define gc_list_validate_space (l , s ) do{}while(0)
462472#endif
463473
464474/*** end of list stuff ***/
@@ -949,6 +959,7 @@ handle_weakrefs(PyGC_Head *unreachable, PyGC_Head *old)
949959 /* Invoke the callbacks we decided to honor. It's safe to invoke them
950960 * because they can't reference unreachable objects.
951961 */
962+ int visited_space = get_gc_state ()-> visited_space ;
952963 while (! gc_list_is_empty (& wrcb_to_call )) {
953964 PyObject * temp ;
954965 PyObject * callback ;
@@ -983,6 +994,7 @@ handle_weakrefs(PyGC_Head *unreachable, PyGC_Head *old)
983994 Py_DECREF (op );
984995 if (wrcb_to_call ._gc_next == (uintptr_t )gc ) {
985996 /* object is still alive -- move it */
997+ gc_set_old_space (gc , visited_space );
986998 gc_list_move (gc , old );
987999 }
9881000 else {
@@ -1389,6 +1401,14 @@ completed_cycle(GCState *gcstate)
13891401 assert (gc_list_is_empty (not_visited ));
13901402#endif
13911403 gcstate -> visited_space = flip_old_space (gcstate -> visited_space );
1404+ /* Make sure all young objects have old space bit set correctly */
1405+ PyGC_Head * young = & gcstate -> young .head ;
1406+ PyGC_Head * gc = GC_NEXT (young );
1407+ while (gc != young ) {
1408+ PyGC_Head * next = GC_NEXT (gc );
1409+ gc_set_old_space (gc , gcstate -> visited_space );
1410+ gc = next ;
1411+ }
13921412 gcstate -> work_to_do = 0 ;
13931413}
13941414
@@ -1406,10 +1426,7 @@ gc_collect_increment(PyThreadState *tstate, struct gc_collection_stats *stats)
14061426 }
14071427 gc_list_merge (& gcstate -> young .head , & increment );
14081428 gcstate -> young .count = 0 ;
1409- if (gcstate -> visited_space ) {
1410- /* objects in visited space have bit set, so we set it here */
1411- gc_list_set_space (& increment , 1 );
1412- }
1429+ gc_list_validate_space (& increment , gcstate -> visited_space );
14131430 Py_ssize_t increment_size = 0 ;
14141431 while (increment_size < gcstate -> work_to_do ) {
14151432 if (gc_list_is_empty (not_visited )) {
@@ -1421,9 +1438,11 @@ gc_collect_increment(PyThreadState *tstate, struct gc_collection_stats *stats)
14211438 gc_set_old_space (gc , gcstate -> visited_space );
14221439 increment_size += expand_region_transitively_reachable (& increment , gc , gcstate );
14231440 }
1441+ gc_list_validate_space (& increment , gcstate -> visited_space );
14241442 PyGC_Head survivors ;
14251443 gc_list_init (& survivors );
14261444 gc_collect_region (tstate , & increment , & survivors , UNTRACK_TUPLES , stats );
1445+ gc_list_validate_space (& survivors , gcstate -> visited_space );
14271446 gc_list_merge (& survivors , visited );
14281447 assert (gc_list_is_empty (& increment ));
14291448 gcstate -> work_to_do += gcstate -> heap_size / SCAN_RATE_DIVISOR / scale_factor ;
@@ -1444,23 +1463,18 @@ gc_collect_full(PyThreadState *tstate,
14441463 GCState * gcstate = & tstate -> interp -> gc ;
14451464 validate_old (gcstate );
14461465 PyGC_Head * young = & gcstate -> young .head ;
1447- PyGC_Head * old0 = & gcstate -> old [0 ].head ;
1448- PyGC_Head * old1 = & gcstate -> old [1 ].head ;
1449- /* merge all generations into old0 */
1450- gc_list_merge (young , old0 );
1466+ PyGC_Head * pending = & gcstate -> old [gcstate -> visited_space ^1 ].head ;
1467+ PyGC_Head * visited = & gcstate -> old [gcstate -> visited_space ].head ;
1468+ /* merge all generations into visited */
1469+ gc_list_validate_space (young , gcstate -> visited_space );
1470+ gc_list_set_space (pending , gcstate -> visited_space );
1471+ gc_list_merge (young , pending );
14511472 gcstate -> young .count = 0 ;
1452- PyGC_Head * gc = GC_NEXT (old1 );
1453- while (gc != old1 ) {
1454- PyGC_Head * next = GC_NEXT (gc );
1455- gc_set_old_space (gc , 0 );
1456- gc = next ;
1457- }
1458- gc_list_merge (old1 , old0 );
1473+ gc_list_merge (pending , visited );
14591474
1460- gc_collect_region (tstate , old0 , old0 ,
1475+ gc_collect_region (tstate , visited , visited ,
14611476 UNTRACK_TUPLES | UNTRACK_DICTS ,
14621477 stats );
1463- gcstate -> visited_space = 1 ;
14641478 gcstate -> young .count = 0 ;
14651479 gcstate -> old [0 ].count = 0 ;
14661480 gcstate -> old [1 ].count = 0 ;
@@ -1527,6 +1541,7 @@ gc_collect_region(PyThreadState *tstate,
15271541
15281542 /* Clear weakrefs and invoke callbacks as necessary. */
15291543 stats -> collected += handle_weakrefs (& unreachable , to );
1544+ gc_list_validate_space (to , gcstate -> visited_space );
15301545 validate_list (to , collecting_clear_unreachable_clear );
15311546 validate_list (& unreachable , collecting_set_unreachable_clear );
15321547
@@ -1560,6 +1575,7 @@ gc_collect_region(PyThreadState *tstate,
15601575 * this if they insist on creating this type of structure.
15611576 */
15621577 handle_legacy_finalizers (tstate , gcstate , & finalizers , to );
1578+ gc_list_validate_space (to , gcstate -> visited_space );
15631579 validate_list (to , collecting_clear_unreachable_clear );
15641580}
15651581
@@ -1708,6 +1724,10 @@ void
17081724_PyGC_Freeze (PyInterpreterState * interp )
17091725{
17101726 GCState * gcstate = & interp -> gc ;
1727+ /* The permanent_generation has its old space bit set to zero */
1728+ if (gcstate -> visited_space ) {
1729+ gc_list_set_space (& gcstate -> young .head , 0 );
1730+ }
17111731 gc_list_merge (& gcstate -> young .head , & gcstate -> permanent_generation .head );
17121732 gcstate -> young .count = 0 ;
17131733 PyGC_Head * old0 = & gcstate -> old [0 ].head ;
0 commit comments