@@ -1040,7 +1040,9 @@ impl GC {
10401040 // Lua 5.5: GCScallfin calls pending finalizers from 'tobefnz'.
10411041 // We delegate the actual calls to the VM via pending_actions.
10421042 if !self . tobefnz . is_empty ( ) {
1043- self . pending_actions . to_finalize . extend ( self . tobefnz . drain ( ..) ) ;
1043+ self . pending_actions
1044+ . to_finalize
1045+ . extend ( self . tobefnz . drain ( ..) ) ;
10441046 }
10451047 self . gc_state = GcState :: Pause ;
10461048 StepResult :: Pause
@@ -1187,6 +1189,52 @@ impl GC {
11871189 /// CRITICAL: Only mark WHITE objects. Black objects from previous cycle became
11881190 /// "other white" after color flip, so they WILL be marked again.
11891191 /// Fixed objects (metamethod names) are kept GRAY forever, so they're naturally skipped.
1192+
1193+ /// Mark OLD1 objects for young collection (port of Lua 5.5's markold).
1194+ /// In generational mode, OLD1 objects are those that just became old in the
1195+ /// previous cycle. They need to be traversed because they might reference
1196+ /// young objects that need to be marked.
1197+ ///
1198+ /// Port of Lua 5.5 lgc.c:
1199+ /// ```c
1200+ /// static void markold (global_State *g, GCObject *from, GCObject *to) {
1201+ /// GCObject *p;
1202+ /// for (p = from; p != to; p = p->next) {
1203+ /// if (getage(p) == G_OLD1) {
1204+ /// lua_assert(!iswhite(p));
1205+ /// setage(p, G_OLD); /* now they are old */
1206+ /// if (isblack(p))
1207+ /// reallymarkobject(g, p);
1208+ /// }
1209+ /// }
1210+ /// }
1211+ /// ```
1212+ fn mark_old1 ( & mut self ) {
1213+ // Iterate through all objects in gc_pool
1214+ // Find OLD1 objects and re-mark them if they're black
1215+ for i in 0 ..self . gc_pool . len ( ) {
1216+ if let Some ( owner) = self . gc_pool . get ( i) {
1217+ let gc_ptr = owner. as_gc_ptr ( ) ;
1218+ if let Some ( header) = gc_ptr. header_mut ( ) {
1219+ if header. age ( ) == G_OLD1 {
1220+ // Assert: OLD1 objects should NOT be white
1221+ debug_assert ! ( !header. is_white( ) , "OLD1 object should not be white" ) ;
1222+
1223+ // Advance age to G_OLD
1224+ header. set_age ( G_OLD ) ;
1225+
1226+ // If it's black, re-mark it (add to gray list for traversal)
1227+ if header. is_black ( ) {
1228+ // Make it gray and add to gray list
1229+ header. make_gray ( ) ;
1230+ self . gray . push ( gc_ptr) ;
1231+ }
1232+ }
1233+ }
1234+ }
1235+ }
1236+ }
1237+
11901238 fn mark_value ( & mut self , value : & LuaValue ) {
11911239 let Some ( gc_ptr) = value. as_gc_ptr ( ) else {
11921240 return ;
@@ -1821,9 +1869,15 @@ impl GC {
18211869 let max_sweep = if fast { usize:: MAX } else { 100 } ;
18221870 let mut count = 0 ;
18231871 let other_white = 1 - self . current_white ;
1872+ let mut total_iterations = 0usize ;
18241873
18251874 // Sweep forward through pool, handling swap_remove correctly
18261875 while self . sweep_index < self . gc_pool . len ( ) && count < max_sweep {
1876+ total_iterations += 1 ;
1877+ if total_iterations > 10000 {
1878+ break ;
1879+ }
1880+
18271881 let current_idx = self . sweep_index ;
18281882
18291883 // Get object info
@@ -1832,8 +1886,7 @@ impl GC {
18321886 let header = owner. header ( ) ;
18331887
18341888 debug_assert_eq ! (
1835- header. index,
1836- current_idx,
1889+ header. index, current_idx,
18371890 "GcHeader.index mismatch during sweep (pool corrupted?)"
18381891 ) ;
18391892
@@ -1860,10 +1913,37 @@ impl GC {
18601913 // DON'T increment sweep_index - the object from end was moved here
18611914 // Next iteration will check that moved object
18621915 } else {
1863- // Lua 5.5 sweeplist: surviving objects reset to current white
1916+ // Surviving object - handle differently based on GC mode
18641917 if let Some ( header_mut) = gc_ptr. header_mut ( ) {
1865- header_mut. make_white ( self . current_white ) ;
1866- header_mut. set_age ( G_NEW ) ;
1918+ if self . gc_kind == GcKind :: Inc || self . gc_kind == GcKind :: GenMajor {
1919+ // INCREMENTAL MODE: Lua 5.5's sweeplist
1920+ // All surviving objects go back to white + G_NEW
1921+ // Port of: curr->marked = cast_byte((marked & ~maskgcbits) | white | G_NEW);
1922+ header_mut. make_white ( self . current_white ) ;
1923+ header_mut. set_age ( G_NEW ) ;
1924+ } else {
1925+ // GENERATIONAL MODE (GenMinor): Lua 5.5's sweepgen
1926+ // Only G_NEW objects go back to white; older objects keep their color
1927+ let age = header_mut. age ( ) ;
1928+ if age == G_NEW {
1929+ // New objects go back to white and become G_SURVIVAL
1930+ header_mut. make_white ( self . current_white ) ;
1931+ header_mut. set_age ( G_SURVIVAL ) ;
1932+ } else {
1933+ // Older objects keep their color and advance age
1934+ // Port of Lua 5.5's nextage table
1935+ let next_age = match age {
1936+ G_SURVIVAL => G_OLD1 ,
1937+ G_OLD0 => G_OLD1 ,
1938+ G_OLD1 => G_OLD ,
1939+ G_OLD => G_OLD , // already old
1940+ G_TOUCHED1 => G_TOUCHED1 , // keep same
1941+ G_TOUCHED2 => G_TOUCHED2 , // keep same
1942+ _ => G_OLD ,
1943+ } ;
1944+ header_mut. set_age ( next_age) ;
1945+ }
1946+ }
18671947 }
18681948 self . sweep_index += 1 ; // Move to next
18691949 }
@@ -2091,6 +2171,12 @@ impl GC {
20912171 self . restart_collection ( roots) ;
20922172 self . gc_state = GcState :: Propagate ;
20932173
2174+ // CRITICAL: Mark OLD1 objects before propagation!
2175+ // In generational mode, OLD1 objects are black from the previous cycle.
2176+ // They must be re-traversed so their referenced young objects get marked.
2177+ // Port of Lua 5.5's markold call in youngcollection.
2178+ self . mark_old1 ( ) ;
2179+
20942180 // Propagate all marks
20952181 while !self . gray . is_empty ( ) {
20962182 self . propagate_mark ( ) ;
0 commit comments