@@ -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 {
@@ -1389,6 +1401,14 @@ completed_cycle(GCState *gcstate)
1389
1401
assert (gc_list_is_empty (not_visited ));
1390
1402
#endif
1391
1403
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
+ }
1392
1412
gcstate -> work_to_do = 0 ;
1393
1413
}
1394
1414
@@ -1406,10 +1426,7 @@ gc_collect_increment(PyThreadState *tstate, struct gc_collection_stats *stats)
1406
1426
}
1407
1427
gc_list_merge (& gcstate -> young .head , & increment );
1408
1428
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 );
1413
1430
Py_ssize_t increment_size = 0 ;
1414
1431
while (increment_size < gcstate -> work_to_do ) {
1415
1432
if (gc_list_is_empty (not_visited )) {
@@ -1421,9 +1438,11 @@ gc_collect_increment(PyThreadState *tstate, struct gc_collection_stats *stats)
1421
1438
gc_set_old_space (gc , gcstate -> visited_space );
1422
1439
increment_size += expand_region_transitively_reachable (& increment , gc , gcstate );
1423
1440
}
1441
+ gc_list_validate_space (& increment , gcstate -> visited_space );
1424
1442
PyGC_Head survivors ;
1425
1443
gc_list_init (& survivors );
1426
1444
gc_collect_region (tstate , & increment , & survivors , UNTRACK_TUPLES , stats );
1445
+ gc_list_validate_space (& survivors , gcstate -> visited_space );
1427
1446
gc_list_merge (& survivors , visited );
1428
1447
assert (gc_list_is_empty (& increment ));
1429
1448
gcstate -> work_to_do += gcstate -> heap_size / SCAN_RATE_DIVISOR / scale_factor ;
@@ -1444,23 +1463,18 @@ gc_collect_full(PyThreadState *tstate,
1444
1463
GCState * gcstate = & tstate -> interp -> gc ;
1445
1464
validate_old (gcstate );
1446
1465
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 );
1451
1472
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 );
1459
1474
1460
- gc_collect_region (tstate , old0 , old0 ,
1475
+ gc_collect_region (tstate , visited , visited ,
1461
1476
UNTRACK_TUPLES | UNTRACK_DICTS ,
1462
1477
stats );
1463
- gcstate -> visited_space = 1 ;
1464
1478
gcstate -> young .count = 0 ;
1465
1479
gcstate -> old [0 ].count = 0 ;
1466
1480
gcstate -> old [1 ].count = 0 ;
@@ -1527,6 +1541,7 @@ gc_collect_region(PyThreadState *tstate,
1527
1541
1528
1542
/* Clear weakrefs and invoke callbacks as necessary. */
1529
1543
stats -> collected += handle_weakrefs (& unreachable , to );
1544
+ gc_list_validate_space (to , gcstate -> visited_space );
1530
1545
validate_list (to , collecting_clear_unreachable_clear );
1531
1546
validate_list (& unreachable , collecting_set_unreachable_clear );
1532
1547
@@ -1560,6 +1575,7 @@ gc_collect_region(PyThreadState *tstate,
1560
1575
* this if they insist on creating this type of structure.
1561
1576
*/
1562
1577
handle_legacy_finalizers (tstate , gcstate , & finalizers , to );
1578
+ gc_list_validate_space (to , gcstate -> visited_space );
1563
1579
validate_list (to , collecting_clear_unreachable_clear );
1564
1580
}
1565
1581
@@ -1708,6 +1724,10 @@ void
1708
1724
_PyGC_Freeze (PyInterpreterState * interp )
1709
1725
{
1710
1726
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
+ }
1711
1731
gc_list_merge (& gcstate -> young .head , & gcstate -> permanent_generation .head );
1712
1732
gcstate -> young .count = 0 ;
1713
1733
PyGC_Head * old0 = & gcstate -> old [0 ].head ;
0 commit comments