@@ -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 {
@@ -1390,6 +1402,14 @@ completed_cycle(GCState *gcstate)
13901402 assert (gc_list_is_empty (not_visited ));
13911403#endif
13921404 gcstate -> visited_space = flip_old_space (gcstate -> visited_space );
1405+ /* Make sure all young objects have old space bit set correctly */
1406+ PyGC_Head * young = & gcstate -> young .head ;
1407+ PyGC_Head * gc = GC_NEXT (young );
1408+ while (gc != young ) {
1409+ PyGC_Head * next = GC_NEXT (gc );
1410+ gc_set_old_space (gc , gcstate -> visited_space );
1411+ gc = next ;
1412+ }
13931413 gcstate -> work_to_do = 0 ;
13941414}
13951415
@@ -1407,10 +1427,7 @@ gc_collect_increment(PyThreadState *tstate, struct gc_collection_stats *stats)
14071427 }
14081428 gc_list_merge (& gcstate -> young .head , & increment );
14091429 gcstate -> young .count = 0 ;
1410- if (gcstate -> visited_space ) {
1411- /* objects in visited space have bit set, so we set it here */
1412- gc_list_set_space (& increment , 1 );
1413- }
1430+ gc_list_validate_space (& increment , gcstate -> visited_space );
14141431 Py_ssize_t increment_size = 0 ;
14151432 while (increment_size < gcstate -> work_to_do ) {
14161433 if (gc_list_is_empty (not_visited )) {
@@ -1422,10 +1439,12 @@ gc_collect_increment(PyThreadState *tstate, struct gc_collection_stats *stats)
14221439 gc_set_old_space (gc , gcstate -> visited_space );
14231440 increment_size += expand_region_transitively_reachable (& increment , gc , gcstate );
14241441 }
1442+ gc_list_validate_space (& increment , gcstate -> visited_space );
14251443 GC_STAT_ADD (1 , objects_queued , region_size );
14261444 PyGC_Head survivors ;
14271445 gc_list_init (& survivors );
14281446 gc_collect_region (tstate , & increment , & survivors , UNTRACK_TUPLES , stats );
1447+ gc_list_validate_space (& survivors , gcstate -> visited_space );
14291448 gc_list_merge (& survivors , visited );
14301449 assert (gc_list_is_empty (& increment ));
14311450 gcstate -> work_to_do += gcstate -> heap_size / SCAN_RATE_DIVISOR / scale_factor ;
@@ -1446,23 +1465,18 @@ gc_collect_full(PyThreadState *tstate,
14461465 GCState * gcstate = & tstate -> interp -> gc ;
14471466 validate_old (gcstate );
14481467 PyGC_Head * young = & gcstate -> young .head ;
1449- PyGC_Head * old0 = & gcstate -> old [0 ].head ;
1450- PyGC_Head * old1 = & gcstate -> old [1 ].head ;
1451- /* merge all generations into old0 */
1452- gc_list_merge (young , old0 );
1468+ PyGC_Head * pending = & gcstate -> old [gcstate -> visited_space ^1 ].head ;
1469+ PyGC_Head * visited = & gcstate -> old [gcstate -> visited_space ].head ;
1470+ /* merge all generations into visited */
1471+ gc_list_validate_space (young , gcstate -> visited_space );
1472+ gc_list_set_space (pending , gcstate -> visited_space );
1473+ gc_list_merge (young , pending );
14531474 gcstate -> young .count = 0 ;
1454- PyGC_Head * gc = GC_NEXT (old1 );
1455- while (gc != old1 ) {
1456- PyGC_Head * next = GC_NEXT (gc );
1457- gc_set_old_space (gc , 0 );
1458- gc = next ;
1459- }
1460- gc_list_merge (old1 , old0 );
1475+ gc_list_merge (pending , visited );
14611476
1462- gc_collect_region (tstate , old0 , old0 ,
1477+ gc_collect_region (tstate , visited , visited ,
14631478 UNTRACK_TUPLES | UNTRACK_DICTS ,
14641479 stats );
1465- gcstate -> visited_space = 1 ;
14661480 gcstate -> young .count = 0 ;
14671481 gcstate -> old [0 ].count = 0 ;
14681482 gcstate -> old [1 ].count = 0 ;
@@ -1529,6 +1543,7 @@ gc_collect_region(PyThreadState *tstate,
15291543
15301544 /* Clear weakrefs and invoke callbacks as necessary. */
15311545 stats -> collected += handle_weakrefs (& unreachable , to );
1546+ gc_list_validate_space (to , gcstate -> visited_space );
15321547 validate_list (to , collecting_clear_unreachable_clear );
15331548 validate_list (& unreachable , collecting_set_unreachable_clear );
15341549
@@ -1562,6 +1577,7 @@ gc_collect_region(PyThreadState *tstate,
15621577 * this if they insist on creating this type of structure.
15631578 */
15641579 handle_legacy_finalizers (tstate , gcstate , & finalizers , to );
1580+ gc_list_validate_space (to , gcstate -> visited_space );
15651581 validate_list (to , collecting_clear_unreachable_clear );
15661582}
15671583
@@ -1710,6 +1726,10 @@ void
17101726_PyGC_Freeze (PyInterpreterState * interp )
17111727{
17121728 GCState * gcstate = & interp -> gc ;
1729+ /* The permanent_generation has its old space bit set to zero */
1730+ if (gcstate -> visited_space ) {
1731+ gc_list_set_space (& gcstate -> young .head , 0 );
1732+ }
17131733 gc_list_merge (& gcstate -> young .head , & gcstate -> permanent_generation .head );
17141734 gcstate -> young .count = 0 ;
17151735 PyGC_Head * old0 = & gcstate -> old [0 ].head ;
0 commit comments