Skip to content

Commit 34825d6

Browse files
committed
Fix Lua::gc_set_mode panic when GC is internally stopped (Lua 5.4/5.5)
1 parent 5548837 commit 34825d6

2 files changed

Lines changed: 37 additions & 8 deletions

File tree

src/state.rs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1170,6 +1170,9 @@ impl Lua {
11701170
/// `None` because Lua's C API does not provide a way to read back current parameter values
11711171
/// without changing them.
11721172
///
1173+
/// If the collector is internally stopped, the mode cannot be changed and the requested mode is
1174+
/// returned as-is.
1175+
///
11731176
/// # Examples
11741177
///
11751178
/// Switch to generational mode (Lua 5.4+):
@@ -1200,9 +1203,8 @@ impl Lua {
12001203
ffi::lua_gc(state, ffi::LUA_GCPARAM, ffi::LUA_GCPSTEPSIZE, v);
12011204
}
12021205
match ffi::lua_gc(state, ffi::LUA_GCINC) {
1203-
ffi::LUA_GCINC => GcMode::Incremental(GcIncParams::default()),
12041206
ffi::LUA_GCGEN => GcMode::Generational(GcGenParams::default()),
1205-
_ => unreachable!(),
1207+
_ => GcMode::Incremental(GcIncParams::default()),
12061208
}
12071209
},
12081210
#[cfg(feature = "lua54")]
@@ -1211,9 +1213,8 @@ impl Lua {
12111213
let step_mul = params.step_multiplier.unwrap_or(0);
12121214
let step_size = params.step_size.unwrap_or(0);
12131215
match ffi::lua_gc(state, ffi::LUA_GCINC, pause, step_mul, step_size) {
1214-
ffi::LUA_GCINC => GcMode::Incremental(GcIncParams::default()),
12151216
ffi::LUA_GCGEN => GcMode::Generational(GcGenParams::default()),
1216-
_ => unreachable!(),
1217+
_ => GcMode::Incremental(GcIncParams::default()),
12171218
}
12181219
},
12191220
#[cfg(any(feature = "lua53", feature = "lua52", feature = "lua51", feature = "luajit"))]
@@ -1252,19 +1253,17 @@ impl Lua {
12521253
ffi::lua_gc(state, ffi::LUA_GCPARAM, ffi::LUA_GCPMAJORMINOR, v);
12531254
}
12541255
match ffi::lua_gc(state, ffi::LUA_GCGEN) {
1255-
ffi::LUA_GCGEN => GcMode::Generational(GcGenParams::default()),
12561256
ffi::LUA_GCINC => GcMode::Incremental(GcIncParams::default()),
1257-
_ => unreachable!(),
1257+
_ => GcMode::Generational(GcGenParams::default()),
12581258
}
12591259
},
12601260
#[cfg(feature = "lua54")]
12611261
GcMode::Generational(params) => unsafe {
12621262
let minor = params.minor_multiplier.unwrap_or(0);
12631263
let minor_to_major = params.minor_to_major.unwrap_or(0);
12641264
match ffi::lua_gc(state, ffi::LUA_GCGEN, minor, minor_to_major) {
1265-
ffi::LUA_GCGEN => GcMode::Generational(GcGenParams::default()),
12661265
ffi::LUA_GCINC => GcMode::Incremental(GcIncParams::default()),
1267-
_ => unreachable!(),
1266+
_ => GcMode::Generational(GcGenParams::default()),
12681267
}
12691268
},
12701269
}

tests/memory.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,36 @@ fn test_gc_control() -> Result<()> {
130130
Ok(())
131131
}
132132

133+
#[cfg(any(feature = "lua54", feature = "lua55"))]
134+
#[test]
135+
fn test_gc_set_mode_in_finalizer() -> Result<()> {
136+
use std::sync::atomic::{AtomicBool, Ordering};
137+
138+
let lua = Lua::new();
139+
140+
// While finalizers are running, the GC is internally stopped and `lua_gc` rejects all
141+
// options
142+
let switched = Arc::new(AtomicBool::new(false));
143+
let switched2 = switched.clone();
144+
let finalizer = lua.create_function(move |lua, ()| {
145+
let mode = lua.gc_set_mode(GcMode::Generational(GcGenParams::default()));
146+
switched2.store(matches!(mode, GcMode::Generational(_)), Ordering::Relaxed);
147+
Ok(())
148+
})?;
149+
lua.globals().set("finalizer", finalizer)?;
150+
lua.load("setmetatable({}, { __gc = finalizer })").exec()?;
151+
lua.globals().remove("finalizer")?;
152+
153+
lua.gc_collect()?;
154+
lua.gc_collect()?;
155+
assert!(
156+
switched.load(Ordering::Relaxed),
157+
"gc_set_mode did not complete in finalizer"
158+
);
159+
160+
Ok(())
161+
}
162+
133163
#[cfg(any(feature = "lua53", feature = "lua52"))]
134164
#[test]
135165
fn test_gc_error() {

0 commit comments

Comments
 (0)