@@ -455,6 +455,30 @@ mark_reachable(PyObject *op)
455
455
}
456
456
457
457
#ifdef GC_DEBUG
458
+ static bool
459
+ validate_refcounts (const mi_heap_t * heap , const mi_heap_area_t * area ,
460
+ void * block , size_t block_size , void * args )
461
+ {
462
+ PyObject * op = op_from_block (block , args , false);
463
+ if (op == NULL ) {
464
+ return true;
465
+ }
466
+
467
+ _PyObject_ASSERT_WITH_MSG (op , !gc_is_unreachable (op ),
468
+ "object should not be marked as unreachable yet" );
469
+
470
+ if (_Py_REF_IS_MERGED (op -> ob_ref_shared )) {
471
+ _PyObject_ASSERT_WITH_MSG (op , op -> ob_tid == 0 ,
472
+ "merged objects should have ob_tid == 0" );
473
+ }
474
+ else if (!_Py_IsImmortal (op )) {
475
+ _PyObject_ASSERT_WITH_MSG (op , op -> ob_tid != 0 ,
476
+ "unmerged objects should have ob_tid != 0" );
477
+ }
478
+
479
+ return true;
480
+ }
481
+
458
482
static bool
459
483
validate_gc_objects (const mi_heap_t * heap , const mi_heap_area_t * area ,
460
484
void * block , size_t block_size , void * args )
@@ -498,6 +522,19 @@ mark_heap_visitor(const mi_heap_t *heap, const mi_heap_area_t *area,
498
522
return true;
499
523
}
500
524
525
+ static bool
526
+ restore_refs (const mi_heap_t * heap , const mi_heap_area_t * area ,
527
+ void * block , size_t block_size , void * args )
528
+ {
529
+ PyObject * op = op_from_block (block , args , false);
530
+ if (op == NULL ) {
531
+ return true;
532
+ }
533
+ gc_restore_tid (op );
534
+ gc_clear_unreachable (op );
535
+ return true;
536
+ }
537
+
501
538
/* Return true if object has a pre-PEP 442 finalization method. */
502
539
static int
503
540
has_legacy_finalizer (PyObject * op )
@@ -549,6 +586,13 @@ static int
549
586
deduce_unreachable_heap (PyInterpreterState * interp ,
550
587
struct collection_state * state )
551
588
{
589
+
590
+ #ifdef GC_DEBUG
591
+ // Check that all objects are marked as unreachable and that the computed
592
+ // reference count difference (stored in `ob_tid`) is non-negative.
593
+ gc_visit_heaps (interp , & validate_refcounts , & state -> base );
594
+ #endif
595
+
552
596
// Identify objects that are directly reachable from outside the GC heap
553
597
// by computing the difference between the refcount and the number of
554
598
// incoming references.
@@ -563,6 +607,8 @@ deduce_unreachable_heap(PyInterpreterState *interp,
563
607
// Transitively mark reachable objects by clearing the
564
608
// _PyGC_BITS_UNREACHABLE flag.
565
609
if (gc_visit_heaps (interp , & mark_heap_visitor , & state -> base ) < 0 ) {
610
+ // On out-of-memory, restore the refcounts and bail out.
611
+ gc_visit_heaps (interp , & restore_refs , & state -> base );
566
612
return -1 ;
567
613
}
568
614
@@ -1066,7 +1112,8 @@ gc_collect_internal(PyInterpreterState *interp, struct collection_state *state,
1066
1112
int err = deduce_unreachable_heap (interp , state );
1067
1113
if (err < 0 ) {
1068
1114
_PyEval_StartTheWorld (interp );
1069
- goto error ;
1115
+ PyErr_NoMemory ();
1116
+ return ;
1070
1117
}
1071
1118
1072
1119
// Print debugging information.
@@ -1100,7 +1147,12 @@ gc_collect_internal(PyInterpreterState *interp, struct collection_state *state,
1100
1147
_PyEval_StartTheWorld (interp );
1101
1148
1102
1149
if (err < 0 ) {
1103
- goto error ;
1150
+ cleanup_worklist (& state -> unreachable );
1151
+ cleanup_worklist (& state -> legacy_finalizers );
1152
+ cleanup_worklist (& state -> wrcb_to_call );
1153
+ cleanup_worklist (& state -> objs_to_decref );
1154
+ PyErr_NoMemory ();
1155
+ return ;
1104
1156
}
1105
1157
1106
1158
// Call tp_clear on objects in the unreachable set. This will cause
@@ -1110,15 +1162,6 @@ gc_collect_internal(PyInterpreterState *interp, struct collection_state *state,
1110
1162
1111
1163
// Append objects with legacy finalizers to the "gc.garbage" list.
1112
1164
handle_legacy_finalizers (state );
1113
- return ;
1114
-
1115
- error :
1116
- cleanup_worklist (& state -> unreachable );
1117
- cleanup_worklist (& state -> legacy_finalizers );
1118
- cleanup_worklist (& state -> wrcb_to_call );
1119
- cleanup_worklist (& state -> objs_to_decref );
1120
- PyErr_NoMemory ();
1121
- PyErr_FormatUnraisable ("Out of memory during garbage collection" );
1122
1165
}
1123
1166
1124
1167
/* This is the main function. Read this to understand how the
0 commit comments