Skip to content

Commit b2c64f4

Browse files
committed
update
1 parent f019ee5 commit b2c64f4

File tree

7 files changed

+115
-19
lines changed

7 files changed

+115
-19
lines changed

crates/luars/src/gc/gc_object.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,6 @@ impl GcHeader {
267267
pub fn is_marked(&self) -> bool {
268268
!self.is_white()
269269
}
270-
271270
}
272271

273272
pub struct Gc<T> {

crates/luars/src/gc/mod.rs

Lines changed: 92 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -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();

crates/luars/src/lua_value/lua_table/hash_table.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ impl LuaTableImpl for LuaHashTable {
8282
self.update_array_len_insert(k);
8383
}
8484
}
85-
85+
8686
result
8787
}
8888

crates/luars/src/lua_value/lua_value.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -955,8 +955,19 @@ impl std::hash::Hash for LuaValue {
955955
} else if tt <= LUA_VFALSE {
956956
// nil or boolean - hash type tag only
957957
tt.hash(state);
958+
} else if self.ttisstring() {
959+
// CRITICAL: Strings must hash by CONTENT, not pointer!
960+
// This maintains the Hash/Eq invariant: if a == b then hash(a) == hash(b)
961+
// PartialEq compares string contents, so Hash must also use contents
962+
tt.hash(state);
963+
if let Some(s) = self.as_str() {
964+
s.hash(state);
965+
} else {
966+
// Fallback: use pointer if we can't get the string content
967+
self.raw_ptr_repr().hash(state);
968+
}
958969
} else {
959-
// GC types: hash type tag + gc_id
970+
// Other GC types: hash type tag + pointer (they use identity for equality)
960971
tt.hash(state);
961972
self.raw_ptr_repr().hash(state);
962973
}

crates/luars/src/lua_vm/execute/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1059,7 +1059,6 @@ pub fn lua_execute_until(lua_state: &mut LuaState, target_depth: usize) -> LuaRe
10591059
let a = instr.get_a() as usize;
10601060
let b = instr.get_b() as usize;
10611061
let c = instr.get_c() as usize;
1062-
10631062
// Save PC before call
10641063
save_pc!();
10651064

@@ -1454,6 +1453,7 @@ pub fn lua_execute_until(lua_state: &mut LuaState, target_depth: usize) -> LuaRe
14541453
lua_state.error(format!("GETTABUP: invalid upvalue index {}", b))
14551454
);
14561455
}
1456+
14571457
let table_value = upvalue_ptrs[b].as_ref().data.get_value(lua_state);
14581458

14591459
// Get key from constants (K[C])

crates/luars/src/lua_vm/mod.rs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -549,11 +549,10 @@ impl LuaVM {
549549
// Collect roots
550550
let roots = self.collect_roots();
551551

552-
// CRITICAL: Mark open upvalues before entering atomic phase.
553-
// In Lua, remarkupvals happens during atomic; we keep the same requirement
554-
// by letting the VM push open upvalues into the GC marking frontier.
555-
let open_upvals = self.main_state.get_open_upvalues();
556-
self.gc.mark_open_upvalues(&open_upvals, &self.main_state);
552+
// NOTE: mark_open_upvalues should NOT be called here!
553+
// In Lua 5.5, remarkupvals is called DURING the atomic phase,
554+
// after restart_collection. Calling it here would add objects
555+
// to the gray list, which then gets cleared by restart_collection.
557556

558557
// If we're keeping invariant (in marking phase), sweep first
559558
if self.gc.keep_invariant() {
@@ -587,9 +586,8 @@ impl LuaVM {
587586
fn full_gen(&mut self) {
588587
let roots = self.collect_roots();
589588

590-
// Same as incremental: ensure open upvalues are marked as part of the root set.
591-
let open_upvals = self.main_state.get_open_upvalues();
592-
self.gc.mark_open_upvalues(&open_upvals, &self.main_state);
589+
// NOTE: mark_open_upvalues should NOT be called here!
590+
// In Lua 5.5, remarkupvals is called DURING the atomic phase.
593591
self.gc.full_generation(&roots, &mut self.object_allocator);
594592
}
595593

crates/luars/src/stdlib/basic/require.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,9 @@ pub fn lua_require(l: &mut LuaState) -> LuaResult<usize> {
7272
// Try each searcher (iterate until we hit nil)
7373
let mut i = 1;
7474
loop {
75-
let searcher = searchers_table.raw_geti(i as i64).unwrap_or(LuaValue::nil());
75+
let searcher = searchers_table
76+
.raw_geti(i as i64)
77+
.unwrap_or(LuaValue::nil());
7678

7779
if searcher.is_nil() {
7880
break;

0 commit comments

Comments
 (0)