@@ -345,6 +345,8 @@ gc_list_merge(PyGC_Head *from, PyGC_Head *to)
345
345
PyGC_Head * from_tail = GC_PREV (from );
346
346
assert (from_head != from );
347
347
assert (from_tail != from );
348
+ assert (gc_list_is_empty (to ) ||
349
+ gc_old_space (to_tail ) == gc_old_space (from_tail ));
348
350
349
351
_PyGCHead_SET_NEXT (to_tail , from_head );
350
352
_PyGCHead_SET_PREV (from_head , to_tail );
@@ -413,8 +415,8 @@ enum flagstates {collecting_clear_unreachable_clear,
413
415
static void
414
416
validate_list (PyGC_Head * head , enum flagstates flags )
415
417
{
416
- assert ((head -> _gc_prev & PREV_MASK_COLLECTING ) == 0 );
417
- assert ((head -> _gc_next & NEXT_MASK_UNREACHABLE ) == 0 );
418
+ assert ((head -> _gc_prev & ~ _PyGC_PREV_MASK ) == 0 );
419
+ assert ((head -> _gc_next & ~ _PyGC_PREV_MASK ) == 0 );
418
420
uintptr_t prev_value = 0 , next_value = 0 ;
419
421
switch (flags ) {
420
422
case collecting_clear_unreachable_clear :
@@ -488,9 +490,37 @@ validate_list_is_aging(PyGC_Head *head)
488
490
assert (prev == GC_PREV (head ));
489
491
}
490
492
493
+ static void
494
+ validate_consistent_old_space (PyGC_Head * head )
495
+ {
496
+ PyGC_Head * prev = head ;
497
+ PyGC_Head * gc = GC_NEXT (head );
498
+ if (gc == head ) {
499
+ return ;
500
+ }
501
+ uintptr_t old_space = gc_old_space (gc );
502
+ while (gc != head ) {
503
+ PyGC_Head * truenext = GC_NEXT (gc );
504
+ assert (truenext != NULL );
505
+ assert (gc_old_space (gc ) == old_space );
506
+ prev = gc ;
507
+ gc = truenext ;
508
+ }
509
+ assert (prev == GC_PREV (head ));
510
+ }
511
+
512
+ static void
513
+ validate_list_header (PyGC_Head * head )
514
+ {
515
+ assert (GC_PREV (head ) == (PyGC_Head * )head -> _gc_prev );
516
+ assert (GC_NEXT (head ) == (PyGC_Head * )head -> _gc_next );
517
+ }
518
+
491
519
#else
492
520
#define validate_old (g ) do{}while(0)
521
+ #define validate_list_header (g ) do{}while(0)
493
522
#define validate_list_is_aging (l ) do{}while(0)
523
+ #define validate_consistent_old_space (l ) do{}while(0)
494
524
#endif
495
525
496
526
/*** end of list stuff ***/
@@ -618,7 +648,7 @@ visit_reachable(PyObject *op, PyGC_Head *reachable)
618
648
prev -> _gc_next & NEXT_MASK_UNREACHABLE );
619
649
_PyObject_ASSERT (FROM_GC (next ),
620
650
next -> _gc_next & NEXT_MASK_UNREACHABLE );
621
- prev -> _gc_next = gc -> _gc_next ; // copy NEXT_MASK_UNREACHABLE
651
+ prev -> _gc_next = gc -> _gc_next ; // copy flag bits
622
652
gc -> _gc_next &= ~NEXT_MASK_UNREACHABLE ;
623
653
_PyGCHead_SET_PREV (next , prev );
624
654
@@ -670,6 +700,9 @@ move_unreachable(PyGC_Head *young, PyGC_Head *unreachable)
670
700
* or to the right have been scanned yet.
671
701
*/
672
702
703
+ validate_consistent_old_space (young );
704
+ /* Record which old space we are in, and set NEXT_MASK_UNREACHABLE bit for convenience */
705
+ uintptr_t flags = NEXT_MASK_UNREACHABLE | (gc -> _gc_next & _PyGC_NEXT_MASK_OLD_SPACE_1 );
673
706
while (gc != young ) {
674
707
if (gc_get_refs (gc )) {
675
708
/* gc is definitely reachable from outside the
@@ -715,15 +748,16 @@ move_unreachable(PyGC_Head *young, PyGC_Head *unreachable)
715
748
// But this may pollute the unreachable list head's 'next' pointer
716
749
// too. That's semantically senseless but expedient here - the
717
750
// damage is repaired when this function ends.
718
- last -> _gc_next = ( NEXT_MASK_UNREACHABLE | (uintptr_t )gc ) ;
751
+ last -> _gc_next = flags | (uintptr_t )gc ;
719
752
_PyGCHead_SET_PREV (gc , last );
720
- gc -> _gc_next = ( NEXT_MASK_UNREACHABLE | (uintptr_t )unreachable ) ;
753
+ gc -> _gc_next = flags | (uintptr_t )unreachable ;
721
754
unreachable -> _gc_prev = (uintptr_t )gc ;
722
755
}
723
756
gc = _PyGCHead_NEXT (prev );
724
757
}
725
758
// young->_gc_prev must be last element remained in the list.
726
759
young -> _gc_prev = (uintptr_t )prev ;
760
+ young -> _gc_next &= _PyGC_PREV_MASK ;
727
761
// don't let the pollution of the list head's next pointer leak
728
762
unreachable -> _gc_next &= _PyGC_PREV_MASK ;
729
763
}
@@ -782,8 +816,8 @@ move_legacy_finalizers(PyGC_Head *unreachable, PyGC_Head *finalizers)
782
816
PyObject * op = FROM_GC (gc );
783
817
784
818
_PyObject_ASSERT (op , gc -> _gc_next & NEXT_MASK_UNREACHABLE );
785
- gc -> _gc_next &= _PyGC_PREV_MASK ;
786
- next = ( PyGC_Head * ) gc -> _gc_next ;
819
+ next = GC_NEXT ( gc ) ;
820
+ gc -> _gc_next &= ~ NEXT_MASK_UNREACHABLE ;
787
821
788
822
if (has_legacy_finalizer (op )) {
789
823
gc_clear_collecting (gc );
@@ -802,8 +836,8 @@ clear_unreachable_mask(PyGC_Head *unreachable)
802
836
assert ((unreachable -> _gc_next & NEXT_MASK_UNREACHABLE ) == 0 );
803
837
for (gc = GC_NEXT (unreachable ); gc != unreachable ; gc = next ) {
804
838
_PyObject_ASSERT ((PyObject * )FROM_GC (gc ), gc -> _gc_next & NEXT_MASK_UNREACHABLE );
805
- gc -> _gc_next &= _PyGC_PREV_MASK ;
806
- next = ( PyGC_Head * ) gc -> _gc_next ;
839
+ next = GC_NEXT ( gc ) ;
840
+ gc -> _gc_next &= ~ NEXT_MASK_UNREACHABLE ;
807
841
}
808
842
validate_list (unreachable , collecting_set_unreachable_clear );
809
843
}
@@ -1181,8 +1215,11 @@ deduce_unreachable(PyGC_Head *base, PyGC_Head *unreachable) {
1181
1215
* refcount greater than 0 when all the references within the
1182
1216
* set are taken into account).
1183
1217
*/
1218
+ validate_list_header (base );
1184
1219
update_refs (base ); // gc_prev is used for gc_refs
1220
+ validate_list_header (base );
1185
1221
subtract_refs (base );
1222
+ validate_list_header (base );
1186
1223
1187
1224
/* Leave everything reachable from outside base in base, and move
1188
1225
* everything else (in base) to unreachable.
@@ -1219,7 +1256,6 @@ deduce_unreachable(PyGC_Head *base, PyGC_Head *unreachable) {
1219
1256
* the reachable objects instead. But this is a one-time cost, probably not
1220
1257
* worth complicating the code to speed just a little.
1221
1258
*/
1222
- gc_list_init (unreachable );
1223
1259
move_unreachable (base , unreachable ); // gc_prev is pointer again
1224
1260
validate_list (base , collecting_clear_unreachable_clear );
1225
1261
validate_list (unreachable , collecting_set_unreachable_set );
@@ -1244,13 +1280,16 @@ handle_resurrected_objects(PyGC_Head *unreachable, PyGC_Head* still_unreachable,
1244
1280
{
1245
1281
// Remove the PREV_MASK_COLLECTING from unreachable
1246
1282
// to prepare it for a new call to 'deduce_unreachable'
1283
+ validate_list_header (unreachable );
1247
1284
gc_list_clear_collecting (unreachable );
1248
1285
1249
1286
// After the call to deduce_unreachable, the 'still_unreachable' set will
1250
1287
// have the PREV_MARK_COLLECTING set, but the objects are going to be
1251
1288
// removed so we can skip the expense of clearing the flag.
1252
1289
PyGC_Head * resurrected = unreachable ;
1290
+ validate_list_header (resurrected );
1253
1291
deduce_unreachable (resurrected , still_unreachable );
1292
+ validate_list_header (resurrected );
1254
1293
clear_unreachable_mask (still_unreachable );
1255
1294
1256
1295
// Move the resurrected objects to the old generation for future collection.
@@ -1437,7 +1476,6 @@ gc_collect_increment(PyThreadState *tstate, struct gc_collection_stats *stats)
1437
1476
PyGC_Head increment ;
1438
1477
gc_list_init (& increment );
1439
1478
Py_ssize_t work_to_do = - gcstate -> incremental_gc_progress ;
1440
- Py_ssize_t region_size = 0 ;
1441
1479
validate_old (gcstate );
1442
1480
if (gc_list_is_empty (oldest )) {
1443
1481
if (gc_list_is_empty (aging )) {
@@ -1453,6 +1491,7 @@ gc_collect_increment(PyThreadState *tstate, struct gc_collection_stats *stats)
1453
1491
gcstate -> aging_space = flip_old_space (gcstate -> aging_space );
1454
1492
}
1455
1493
validate_old (gcstate );
1494
+ Py_ssize_t region_size = 0 ;
1456
1495
while (region_size < work_to_do ) {
1457
1496
if (gc_list_is_empty (oldest )) {
1458
1497
gcstate -> incremental_gc_progress = 0 ;
@@ -1465,17 +1504,12 @@ gc_collect_increment(PyThreadState *tstate, struct gc_collection_stats *stats)
1465
1504
}
1466
1505
validate_old (gcstate );
1467
1506
validate_list_is_aging (& increment );
1468
- #ifdef Py_STATS
1469
- {
1470
- Py_ssize_t count = 0 ;
1471
- PyGC_Head * gc ;
1472
- for (gc = GC_NEXT (& increment ); gc != & increment ; gc = GC_NEXT (gc )) {
1473
- count ++ ;
1474
- }
1475
- GC_STAT_ADD (NUM_GENERATIONS , objects_queued , count );
1476
- }
1477
- #endif
1478
- gc_collect_region (tstate , & increment , aging , 0 , stats );
1507
+ GC_STAT_ADD (1 , objects_queued , region_size );
1508
+ PyGC_Head survivors ;
1509
+ gc_list_init (& survivors );
1510
+ gc_collect_region (tstate , & increment , & survivors , 0 , stats );
1511
+ validate_list_is_aging (& survivors );
1512
+ gc_list_merge (& survivors , aging );
1479
1513
validate_old (gcstate );
1480
1514
assert (gc_list_is_empty (& increment ));
1481
1515
gcstate -> incremental_gc_progress += region_size ;
@@ -1525,8 +1559,6 @@ gc_collect_region(PyThreadState *tstate,
1525
1559
int untrack ,
1526
1560
struct gc_collection_stats * stats )
1527
1561
{
1528
- Py_ssize_t m = 0 ; /* # objects collected */
1529
- Py_ssize_t n = 0 ; /* # unreachable objects that couldn't be collected */
1530
1562
PyGC_Head unreachable ; /* non-problematic unreachable trash */
1531
1563
PyGC_Head finalizers ; /* objects with, & reachable from, __del__ */
1532
1564
PyGC_Head * gc ; /* initialize to prevent a compiler warning */
@@ -1537,17 +1569,23 @@ gc_collect_region(PyThreadState *tstate,
1537
1569
1538
1570
validate_list (from , collecting_clear_unreachable_clear );
1539
1571
validate_list (to , collecting_clear_unreachable_clear );
1572
+ validate_consistent_old_space (from );
1573
+ validate_consistent_old_space (to );
1540
1574
1575
+ gc_list_init (& unreachable );
1541
1576
deduce_unreachable (from , & unreachable );
1577
+ validate_consistent_old_space (from );
1542
1578
if (untrack & UNTRACK_TUPLES ) {
1543
1579
untrack_tuples (from );
1544
1580
}
1545
1581
if (untrack & UNTRACK_DICTS ) {
1546
1582
untrack_dicts (from );
1547
1583
}
1584
+ validate_consistent_old_space (to );
1548
1585
if (from != to ) {
1549
1586
gc_list_merge (from , to );
1550
1587
}
1588
+ validate_consistent_old_space (to );
1551
1589
validate_old (gcstate );
1552
1590
/* Move reachable objects to next generation. */
1553
1591
@@ -1558,11 +1596,14 @@ gc_collect_region(PyThreadState *tstate,
1558
1596
// NEXT_MASK_UNREACHABLE is cleared here.
1559
1597
// After move_legacy_finalizers(), unreachable is normal list.
1560
1598
move_legacy_finalizers (& unreachable , & finalizers );
1599
+ validate_consistent_old_space (& unreachable );
1600
+ validate_consistent_old_space (& finalizers );
1561
1601
/* finalizers contains the unreachable objects with a legacy finalizer;
1562
1602
* unreachable objects reachable *from* those are also uncollectable,
1563
1603
* and we move those into the finalizers list too.
1564
1604
*/
1565
1605
move_legacy_finalizer_reachable (& finalizers );
1606
+ validate_consistent_old_space (& finalizers );
1566
1607
1567
1608
validate_list (& finalizers , collecting_clear_unreachable_clear );
1568
1609
validate_list (& unreachable , collecting_set_unreachable_clear );
@@ -1575,44 +1616,51 @@ gc_collect_region(PyThreadState *tstate,
1575
1616
}
1576
1617
1577
1618
/* Clear weakrefs and invoke callbacks as necessary. */
1578
- m += handle_weakrefs (& unreachable , to );
1619
+ stats -> collected += handle_weakrefs (& unreachable , to );
1620
+ validate_consistent_old_space (to );
1579
1621
1580
1622
validate_list (to , collecting_clear_unreachable_clear );
1581
1623
validate_list (& unreachable , collecting_set_unreachable_clear );
1582
1624
1583
1625
/* Call tp_finalize on objects which have one. */
1584
1626
finalize_garbage (tstate , & unreachable );
1627
+ validate_consistent_old_space (& unreachable );
1585
1628
1586
1629
/* Handle any objects that may have resurrected after the call
1587
1630
* to 'finalize_garbage' and continue the collection with the
1588
1631
* objects that are still unreachable */
1589
1632
PyGC_Head final_unreachable ;
1633
+ gc_list_init (& final_unreachable );
1634
+ validate_consistent_old_space (to );
1590
1635
handle_resurrected_objects (& unreachable , & final_unreachable , to );
1636
+ validate_consistent_old_space (to );
1591
1637
1592
1638
/* Call tp_clear on objects in the final_unreachable set. This will cause
1593
1639
* the reference cycles to be broken. It may also cause some objects
1594
1640
* in finalizers to be freed.
1595
1641
*/
1596
- m += gc_list_size (& final_unreachable );
1642
+ stats -> collected += gc_list_size (& final_unreachable );
1597
1643
delete_garbage (tstate , gcstate , & final_unreachable , to );
1644
+ validate_consistent_old_space (to );
1598
1645
1599
1646
/* Collect statistics on uncollectable objects found and print
1600
1647
* debugging information. */
1648
+ Py_ssize_t n = 0 ;
1601
1649
for (gc = GC_NEXT (& finalizers ); gc != & finalizers ; gc = GC_NEXT (gc )) {
1602
1650
n ++ ;
1603
1651
if (gcstate -> debug & DEBUG_UNCOLLECTABLE )
1604
1652
debug_cycle ("uncollectable" , FROM_GC (gc ));
1605
1653
}
1606
1654
1655
+ stats -> uncollectable = n ;
1607
1656
/* Append instances in the uncollectable set to a Python
1608
1657
* reachable list of garbage. The programmer has to deal with
1609
1658
* this if they insist on creating this type of structure.
1610
1659
*/
1611
1660
handle_legacy_finalizers (tstate , gcstate , & finalizers , to );
1612
1661
validate_list (to , collecting_clear_unreachable_clear );
1662
+ validate_consistent_old_space (to );
1613
1663
1614
- stats -> collected = m ;
1615
- stats -> uncollectable = n ;
1616
1664
validate_old (gcstate );
1617
1665
}
1618
1666
0 commit comments