Implement all methods for controlling the Lua GC, and some code cleanup

This commit is contained in:
SeanOMik 2024-02-10 17:04:09 -05:00
parent 25f4116278
commit bed5091ecd
Signed by: SeanOMik
GPG Key ID: FEC9E2FC15235964
4 changed files with 94 additions and 77 deletions

View File

@ -33,71 +33,6 @@ use lref::*;
#[cfg(test)] #[cfg(test)]
pub mod tests; pub mod tests;
/* fn main() -> Result<()> {
let lua = State::new();
lua.expose_libraries(&[StdLibrary::Debug, StdLibrary::Package]);
let globals = lua.globals()?;
let v1 = RefCell::new(Vec2 { x: 50.0, y: 5.0 });
let ud = lua.create_userdata(UserdataRef::from(v1.borrow()))?;
globals.set("v1", ud)?;
let v2 = Vec2 { x: 10.0, y: 15.0 };
let ud = lua.create_userdata(UserdataRef::from(&v2))?;
globals.set("v2", ud)?;
//lua.gc_stop()?;
let chunk = lua.load(
"text.lua",
r#"
require "util"
--if vec2 ~= nil then
print("v1: (" .. v1.x .. ", " .. v1.y .. ")")
--print("v2: (" .. v2.x .. ", " .. v2.y .. ")")
--end
--print("vec2.x = " .. vec2.x)
--print("vec2.y = " .. vec2.y)
"#,
)?;
const MAX_RUNS: i32 = 400;
for i in 0..MAX_RUNS {
// I don't care about the result of this execution, so I set the result as a
// Value which can be anything
//
// Chunks can also be executed with: `chunk.execute(())?;`
let res = lua.execute_chunk::<_, Value>(&chunk, ());
// if unwrapped, the new lines in the message would be escaped making
// the traceback in the error difficult to read.
if let Err(e) = res {
panic!("{}", e);
}
//print_refs(&lua).unwrap();
println!("i = {}", i);
globals.set("v1", Value::Nil)?;
lua.gc_collect()?;
let mut t = v1.borrow_mut();
t.x += 50.0;
t.y += 5.0;
drop(t);
let ud = lua.create_userdata(UserdataRef::from(v1.borrow()))?;
globals.raw_set("v1", ud)?;
}
unsafe {
assert_eq!(lua::lua_gettop(lua.state_ptr()), 0); // ensure that nothing is left on the stack
}
Ok(())
} */
pub trait PushToLuaStack<'a> { pub trait PushToLuaStack<'a> {
unsafe fn push_to_lua_stack(&self, state: &'a State) -> Result<()>; unsafe fn push_to_lua_stack(&self, state: &'a State) -> Result<()>;
} }

View File

@ -4,7 +4,7 @@ use std::{alloc::{self, Layout}, any::TypeId, cell::RefCell, collections::HashMa
use lua::lua_gc; use lua::lua_gc;
use mlua_sys as lua; use mlua_sys as lua;
use crate::{lua_error_guard, AnyUserdata, AsLua, Chunk, Error, FromLua, FromLuaStack, FromLuaVec, Function, IntoChunkData, IntoLuaVec, LuaRef, MetaMethod, PushToLuaStack, Result, StackGuard, Table, Userdata, UserdataBuilder, Value, ValueVec}; use crate::{lua_error_guard, proxy::UserdataProxy, AnyUserdata, AsLua, Chunk, Error, FromLua, FromLuaStack, FromLuaVec, Function, IntoChunkData, IntoLuaVec, LuaRef, MetaMethod, PushToLuaStack, Result, StackGuard, Table, Userdata, UserdataBuilder, Value, ValueVec};
pub fn ptr_to_string(ptr: *const i8) -> std::result::Result<String, Utf8Error> { pub fn ptr_to_string(ptr: *const i8) -> std::result::Result<String, Utf8Error> {
let c = unsafe { CStr::from_ptr(ptr) }; let c = unsafe { CStr::from_ptr(ptr) };
@ -522,6 +522,13 @@ impl State {
Ok(mt) Ok(mt)
} }
/// Creates a userdata proxy. This proxy has the same metatable as `T`, but has no default value.
/// This eliminates the need for `T` to have a default value.
pub fn create_proxy<T: Userdata + 'static>(&self) -> Result<AnyUserdata> {
self.create_userdata(UserdataProxy::<T>::new())
}
/// Retrieves the debug info from the Lua stack.
pub(crate) unsafe fn debug_info(&self) -> Box<lua::lua_Debug> { pub(crate) unsafe fn debug_info(&self) -> Box<lua::lua_Debug> {
let s = self.state_ptr(); let s = self.state_ptr();
@ -535,6 +542,7 @@ impl State {
Box::from_raw(ar) Box::from_raw(ar)
} }
/// Retrieves the current line of the script
pub fn current_line(&self) -> usize { pub fn current_line(&self) -> usize {
unsafe { unsafe {
let ar = self.debug_info(); let ar = self.debug_info();
@ -542,6 +550,7 @@ impl State {
} }
} }
/// Retrieves a traceback of the Lua stack, optionally prepends the traceback string with `msg`
pub fn traceback(&self, msg: Option<&str>) -> Result<String> { pub fn traceback(&self, msg: Option<&str>) -> Result<String> {
unsafe { unsafe {
let _g = StackGuard::new(self); let _g = StackGuard::new(self);
@ -589,24 +598,97 @@ impl State {
} }
/// Triggers a full garbage-collection cycle. /// Triggers a full garbage-collection cycle.
pub fn gc_collect(&self) -> Result<()> { pub fn gc_collect(&self) {
unsafe { unsafe {
let s = self.state_ptr(); let s = self.state_ptr();
lua_gc(s, lua::LUA_GCCOLLECT); lua_gc(s, lua::LUA_GCCOLLECT);
} }
Ok(())
} }
/// Disable the garbage collector /// Disable the garbage collector
pub fn gc_stop(&self) -> Result<()> { pub fn gc_stop(&self) {
unsafe { unsafe {
let s = self.state_ptr(); let s = self.state_ptr();
lua_gc(s, lua::LUA_GCSTOP); lua_gc(s, lua::LUA_GCSTOP);
} }
}
Ok(()) /// Restarts the garbage collector
pub fn gc_restart(&self) {
unsafe {
let s = self.state_ptr();
lua_gc(s, lua::LUA_GCRESTART);
} }
} }
/// Returns the current amount of memory used by Lua in kilabytes
pub fn memory_usage(&self) -> u32 {
unsafe {
let s = self.state_ptr();
lua_gc(s, lua::LUA_GCCOUNT) as u32
}
}
/// Returns the remainder of dividing the current amount of bytes of memory in use by Lua by 1024.
pub fn memory_usage_rem(&self) -> u32 {
unsafe {
let s = self.state_ptr();
lua_gc(s, lua::LUA_GCCOUNTB) as u32
}
}
/// Trigger a step of the garbage collector
///
/// Lua docs:
/// > Performs an incremental step of garbage collection, corresponding to the allocation
/// of stepsize Kbytes.
pub fn gc_step(&self, step_size: i32) {
unsafe {
let s = self.state_ptr();
lua_gc(s, lua::LUA_GCSTEP, step_size);
}
}
/// Changes the collector to incremental mode with the given parameters
/// (see [Incremental Garbage Collection](https://www.lua.org/manual/5.4/manual.html#2.5.1)).
/// Returns the previous mode.
pub fn gc_set_incremental(&self, pause: i32, step_mul: i32, step_size: i32) -> GcMode {
unsafe {
let s = self.state_ptr();
let mode = lua_gc(s, lua::LUA_GCINC, pause, step_mul, step_size);
if mode == lua::LUA_GCGEN {
GcMode::Generational
} else {
GcMode::Incremental
}
}
}
/// Changes the collector to generational mode with the given parameters
/// (see [Generational Garbage Collection](https://www.lua.org/manual/5.4/manual.html#2.5.2)).
/// Returns the previous mode.
pub fn gc_set_generational(&self, minor_mul: i32, major_mul: i32)-> GcMode {
unsafe {
let s = self.state_ptr();
let mode = lua_gc(s, lua::LUA_GCGEN, minor_mul, major_mul);
if mode == lua::LUA_GCGEN {
GcMode::Generational
} else {
GcMode::Incremental
}
}
}
}
pub enum GcMode {
Incremental,
Generational,
}

View File

@ -87,9 +87,9 @@ impl<'a, T: Userdata + 'static> Userdata for UserdataRef<'a, T> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::{borrow::Borrow, cell::{Ref, RefCell}}; use std::cell::{Ref, RefCell};
use crate::{tests::Vec2, AnyUserdata, State, StdLibrary, Value}; use crate::{tests::Vec2, State, StdLibrary, Value};
use super::UserdataRef; use super::UserdataRef;
@ -120,7 +120,7 @@ mod tests {
//println!("i = {}", i); //println!("i = {}", i);
globals.set("v1", Value::Nil)?; globals.set("v1", Value::Nil)?;
lua.gc_collect()?; // must collect here to drop the Ref lua.gc_collect(); // must collect here to drop the Ref
let mut t = v1.borrow_mut(); let mut t = v1.borrow_mut();
t.x += 50.0; t.x += 50.0;

View File

@ -141,7 +141,7 @@ mod tests {
//println!("i = {}", i); //println!("i = {}", i);
globals.set("v1", Value::Nil)?; globals.set("v1", Value::Nil)?;
lua.gc_collect()?; // must collect here to drop the RefMut lua.gc_collect(); // must collect here to drop the RefMut
x += 50.0; x += 50.0;
y += 5.0; y += 5.0;
@ -184,7 +184,7 @@ mod tests {
} }
globals.set("v1", Value::Nil)?; globals.set("v1", Value::Nil)?;
lua.gc_collect()?; // must collect here to drop the RefMut lua.gc_collect(); // must collect here to drop the RefMut
let t = v1.borrow(); let t = v1.borrow();
assert_eq!(100.0, t.x); assert_eq!(100.0, t.x);