@@ -455,10 +455,20 @@ validate_consistent_old_space(PyGC_Head *head)
455
455
assert (prev == GC_PREV (head ));
456
456
}
457
457
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
+
458
467
#else
459
468
#define validate_list (x , y ) do{}while(0)
460
469
#define validate_old (g ) do{}while(0)
461
470
#define validate_consistent_old_space (l ) do{}while(0)
471
+ #define gc_list_validate_space (l , s ) do{}while(0)
462
472
#endif
463
473
464
474
/*** end of list stuff ***/
@@ -949,6 +959,7 @@ handle_weakrefs(PyGC_Head *unreachable, PyGC_Head *old)
949
959
/* Invoke the callbacks we decided to honor. It's safe to invoke them
950
960
* because they can't reference unreachable objects.
951
961
*/
962
+ int visited_space = get_gc_state ()-> visited_space ;
952
963
while (! gc_list_is_empty (& wrcb_to_call )) {
953
964
PyObject * temp ;
954
965
PyObject * callback ;
@@ -983,6 +994,7 @@ handle_weakrefs(PyGC_Head *unreachable, PyGC_Head *old)
983
994
Py_DECREF (op );
984
995
if (wrcb_to_call ._gc_next == (uintptr_t )gc ) {
985
996
/* object is still alive -- move it */
997
+ gc_set_old_space (gc , visited_space );
986
998
gc_list_move (gc , old );
987
999
}
988
1000
else {
@@ -1390,6 +1402,14 @@ completed_cycle(GCState *gcstate)
1390
1402
assert (gc_list_is_empty (not_visited ));
1391
1403
#endif
1392
1404
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
+ }
1393
1413
gcstate -> work_to_do = 0 ;
1394
1414
}
1395
1415
@@ -1407,10 +1427,7 @@ gc_collect_increment(PyThreadState *tstate, struct gc_collection_stats *stats)
1407
1427
}
1408
1428
gc_list_merge (& gcstate -> young .head , & increment );
1409
1429
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 );
1414
1431
Py_ssize_t increment_size = 0 ;
1415
1432
while (increment_size < gcstate -> work_to_do ) {
1416
1433
if (gc_list_is_empty (not_visited )) {
@@ -1422,10 +1439,12 @@ gc_collect_increment(PyThreadState *tstate, struct gc_collection_stats *stats)
1422
1439
gc_set_old_space (gc , gcstate -> visited_space );
1423
1440
increment_size += expand_region_transitively_reachable (& increment , gc , gcstate );
1424
1441
}
1442
+ gc_list_validate_space (& increment , gcstate -> visited_space );
1425
1443
GC_STAT_ADD (1 , objects_queued , region_size );
1426
1444
PyGC_Head survivors ;
1427
1445
gc_list_init (& survivors );
1428
1446
gc_collect_region (tstate , & increment , & survivors , UNTRACK_TUPLES , stats );
1447
+ gc_list_validate_space (& survivors , gcstate -> visited_space );
1429
1448
gc_list_merge (& survivors , visited );
1430
1449
assert (gc_list_is_empty (& increment ));
1431
1450
gcstate -> work_to_do += gcstate -> heap_size / SCAN_RATE_DIVISOR / scale_factor ;
@@ -1446,23 +1465,18 @@ gc_collect_full(PyThreadState *tstate,
1446
1465
GCState * gcstate = & tstate -> interp -> gc ;
1447
1466
validate_old (gcstate );
1448
1467
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 );
1453
1474
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 );
1461
1476
1462
- gc_collect_region (tstate , old0 , old0 ,
1477
+ gc_collect_region (tstate , visited , visited ,
1463
1478
UNTRACK_TUPLES | UNTRACK_DICTS ,
1464
1479
stats );
1465
- gcstate -> visited_space = 1 ;
1466
1480
gcstate -> young .count = 0 ;
1467
1481
gcstate -> old [0 ].count = 0 ;
1468
1482
gcstate -> old [1 ].count = 0 ;
@@ -1529,6 +1543,7 @@ gc_collect_region(PyThreadState *tstate,
1529
1543
1530
1544
/* Clear weakrefs and invoke callbacks as necessary. */
1531
1545
stats -> collected += handle_weakrefs (& unreachable , to );
1546
+ gc_list_validate_space (to , gcstate -> visited_space );
1532
1547
validate_list (to , collecting_clear_unreachable_clear );
1533
1548
validate_list (& unreachable , collecting_set_unreachable_clear );
1534
1549
@@ -1562,6 +1577,7 @@ gc_collect_region(PyThreadState *tstate,
1562
1577
* this if they insist on creating this type of structure.
1563
1578
*/
1564
1579
handle_legacy_finalizers (tstate , gcstate , & finalizers , to );
1580
+ gc_list_validate_space (to , gcstate -> visited_space );
1565
1581
validate_list (to , collecting_clear_unreachable_clear );
1566
1582
}
1567
1583
@@ -1710,6 +1726,10 @@ void
1710
1726
_PyGC_Freeze (PyInterpreterState * interp )
1711
1727
{
1712
1728
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
+ }
1713
1733
gc_list_merge (& gcstate -> young .head , & gcstate -> permanent_generation .head );
1714
1734
gcstate -> young .count = 0 ;
1715
1735
PyGC_Head * old0 = & gcstate -> old [0 ].head ;
0 commit comments