@@ -101,6 +101,8 @@ pub struct GcState {
101101 /// Per-generation object tracking (for correct gc_refs algorithm)
102102 /// Objects start in gen0, survivors move to gen1, then gen2
103103 generation_objects : [ RwLock < HashSet < GcObjectPtr > > ; 3 ] ,
104+ /// Frozen/permanent objects (excluded from normal GC)
105+ permanent_objects : RwLock < HashSet < GcObjectPtr > > ,
104106 /// Debug flags
105107 pub debug : AtomicU32 ,
106108 /// gc.garbage list (uncollectable objects with __del__)
@@ -145,6 +147,7 @@ impl GcState {
145147 RwLock :: new ( HashSet :: new ( ) ) ,
146148 RwLock :: new ( HashSet :: new ( ) ) ,
147149 ] ,
150+ permanent_objects : RwLock :: new ( HashSet :: new ( ) ) ,
148151 debug : AtomicU32 :: new ( 0 ) ,
149152 garbage : PyMutex :: new ( Vec :: new ( ) ) ,
150153 callbacks : PyMutex :: new ( Vec :: new ( ) ) ,
@@ -225,12 +228,14 @@ impl GcState {
225228 /// obj must be a valid pointer to a PyObject
226229 pub unsafe fn track_object ( & self , obj : NonNull < PyObject > ) {
227230 let gc_ptr = GcObjectPtr ( obj) ;
228- self . generations [ 0 ] . count . fetch_add ( 1 , Ordering :: SeqCst ) ;
229- self . alloc_count . fetch_add ( 1 , Ordering :: SeqCst ) ;
230231
231- // Add to generation 0 tracking (for correct gc_refs algorithm)
232- if let Ok ( mut gen0) = self . generation_objects [ 0 ] . write ( ) {
233- gen0. insert ( gc_ptr) ;
232+ // Add to generation 0 tracking first (for correct gc_refs algorithm)
233+ // Only increment count if we successfully add to the set
234+ if let Ok ( mut gen0) = self . generation_objects [ 0 ] . write ( )
235+ && gen0. insert ( gc_ptr)
236+ {
237+ self . generations [ 0 ] . count . fetch_add ( 1 , Ordering :: SeqCst ) ;
238+ self . alloc_count . fetch_add ( 1 , Ordering :: SeqCst ) ;
234239 }
235240
236241 // Also add to global tracking (for get_objects, etc.)
@@ -247,23 +252,20 @@ impl GcState {
247252 pub unsafe fn untrack_object ( & self , obj : NonNull < PyObject > ) {
248253 let gc_ptr = GcObjectPtr ( obj) ;
249254
250- // Remove from all generation tracking lists
251- for generation in & self . generation_objects {
252- if generation
253- . write ( )
254- . ok ( )
255- . is_some_and ( |mut gen_set| gen_set. remove ( & gc_ptr) )
255+ // Remove from generation tracking lists and decrement the correct generation's count
256+ for ( gen_idx, generation) in self . generation_objects . iter ( ) . enumerate ( ) {
257+ if let Ok ( mut gen_set) = generation. write ( )
258+ && gen_set. remove ( & gc_ptr)
256259 {
260+ // Decrement count for the generation we removed from
261+ let count = self . generations [ gen_idx] . count . load ( Ordering :: SeqCst ) ;
262+ if count > 0 {
263+ self . generations [ gen_idx] . count . fetch_sub ( 1 , Ordering :: SeqCst ) ;
264+ }
257265 break ; // Object can only be in one generation
258266 }
259267 }
260268
261- // Update counts (simplified - just decrement gen0 for now)
262- let count = self . generations [ 0 ] . count . load ( Ordering :: SeqCst ) ;
263- if count > 0 {
264- self . generations [ 0 ] . count . fetch_sub ( 1 , Ordering :: SeqCst ) ;
265- }
266-
267269 // Remove from global tracking
268270 if let Ok ( mut tracked) = self . tracked_objects . write ( ) {
269271 tracked. remove ( & gc_ptr) ;
@@ -712,14 +714,21 @@ impl GcState {
712714 for & ptr in survivors {
713715 // Remove from current generation
714716 for gen_idx in 0 ..=from_gen {
715- if self . generation_objects [ gen_idx]
716- . write ( )
717- . ok ( )
718- . is_some_and ( |mut gen_set| gen_set. remove ( & ptr) )
717+ if let Ok ( mut gen_set) = self . generation_objects [ gen_idx] . write ( )
718+ && gen_set. remove ( & ptr)
719719 {
720+ // Decrement count for source generation
721+ let count = self . generations [ gen_idx] . count . load ( Ordering :: SeqCst ) ;
722+ if count > 0 {
723+ self . generations [ gen_idx] . count . fetch_sub ( 1 , Ordering :: SeqCst ) ;
724+ }
725+
720726 // Add to next generation
721- if let Ok ( mut next_set) = self . generation_objects [ next_gen] . write ( ) {
722- next_set. insert ( ptr) ;
727+ if let Ok ( mut next_set) = self . generation_objects [ next_gen] . write ( )
728+ && next_set. insert ( ptr)
729+ {
730+ // Increment count for target generation
731+ self . generations [ next_gen] . count . fetch_add ( 1 , Ordering :: SeqCst ) ;
723732 }
724733 break ;
725734 }
@@ -735,16 +744,42 @@ impl GcState {
735744 /// Freeze all tracked objects (move to permanent generation)
736745 pub fn freeze ( & self ) {
737746 // Move all objects from gen0-2 to permanent
738- for generation in & self . generations {
739- let count = generation. count . swap ( 0 , Ordering :: SeqCst ) ;
747+ let mut objects_to_freeze: Vec < GcObjectPtr > = Vec :: new ( ) ;
748+
749+ for ( gen_idx, generation) in self . generation_objects . iter ( ) . enumerate ( ) {
750+ if let Ok ( mut gen_set) = generation. write ( ) {
751+ objects_to_freeze. extend ( gen_set. drain ( ) ) ;
752+ self . generations [ gen_idx] . count . store ( 0 , Ordering :: SeqCst ) ;
753+ }
754+ }
755+
756+ // Add to permanent set
757+ if let Ok ( mut permanent) = self . permanent_objects . write ( ) {
758+ let count = objects_to_freeze. len ( ) ;
759+ for ptr in objects_to_freeze {
760+ permanent. insert ( ptr) ;
761+ }
740762 self . permanent . count . fetch_add ( count, Ordering :: SeqCst ) ;
741763 }
742764 }
743765
744766 /// Unfreeze all objects (move from permanent to gen2)
745767 pub fn unfreeze ( & self ) {
746- let count = self . permanent . count . swap ( 0 , Ordering :: SeqCst ) ;
747- self . generations [ 2 ] . count . fetch_add ( count, Ordering :: SeqCst ) ;
768+ let mut objects_to_unfreeze: Vec < GcObjectPtr > = Vec :: new ( ) ;
769+
770+ if let Ok ( mut permanent) = self . permanent_objects . write ( ) {
771+ objects_to_unfreeze. extend ( permanent. drain ( ) ) ;
772+ self . permanent . count . store ( 0 , Ordering :: SeqCst ) ;
773+ }
774+
775+ // Add to generation 2
776+ if let Ok ( mut gen2) = self . generation_objects [ 2 ] . write ( ) {
777+ let count = objects_to_unfreeze. len ( ) ;
778+ for ptr in objects_to_unfreeze {
779+ gen2. insert ( ptr) ;
780+ }
781+ self . generations [ 2 ] . count . fetch_add ( count, Ordering :: SeqCst ) ;
782+ }
748783 }
749784}
750785
0 commit comments