use std::cell::RefCell;
use std::collections::HashMap;
use crate::error::{Error, Result};
use crate::state::Lua;
use crate::sys::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum VmState {
Continue,
Yield,
}
type InterruptFn = Box<dyn Fn(&Lua) -> Result<VmState> + 'static>;
thread_local! {
static INTERRUPTS: RefCell<HashMap<*mut core::ffi::c_void, InterruptFn>> =
RefCell::new(HashMap::new());
}
unsafe fn vm_key(state: *mut lua_State) -> *mut core::ffi::c_void {
unsafe { (*state).global as *mut core::ffi::c_void }
}
impl Lua {
pub fn set_interrupt<F>(&self, callback: F)
where
F: Fn(&Lua) -> Result<VmState> + crate::sync::MaybeSend + 'static,
{
let state = self.state();
unsafe {
let key = vm_key(state);
INTERRUPTS.with(|m| {
m.borrow_mut().insert(key, Box::new(callback));
});
let cb = lua_callbacks(state);
(*cb).interrupt = Some(interrupt_trampoline);
}
}
pub fn remove_interrupt(&self) {
let state = self.state();
unsafe {
let key = vm_key(state);
INTERRUPTS.with(|m| {
m.borrow_mut().remove(&key);
});
let cb = lua_callbacks(state);
(*cb).interrupt = None;
}
}
}
unsafe extern "C-unwind" fn interrupt_trampoline(state: *mut lua_State, gc: c_int) {
if gc >= 0 {
return;
}
let key = unsafe { vm_key(state) };
let cb = INTERRUPTS.with(|m| m.borrow_mut().remove(&key));
let Some(cb) = cb else { return };
let lua = unsafe { Lua::from_borrowed(state) };
let result = cb(&lua);
INTERRUPTS.with(|m| {
let mut map = m.borrow_mut();
map.entry(key).or_insert(cb);
});
match result {
Ok(VmState::Continue) => {}
Ok(VmState::Yield) => unsafe {
if lua_isyieldable(state) != 0 {
let _ = luaur_vm::functions::lua_break::lua_break(state);
}
},
Err(e) => unsafe {
raise_error(state, &e);
},
}
}
unsafe fn raise_error(state: *mut lua_State, e: &Error) -> ! {
let msg = match e {
Error::RuntimeError(m) => m.clone(),
other => other.to_string(),
};
unsafe {
lua_rawcheckstack(state, 1);
lua_pushlstring(state, msg.as_ptr() as *const c_char, msg.len());
lua_error(state)
}
}