Make LuaHook as Fn instead of FnMut to remove Mutex and improve performance

This commit is contained in:
Alex Orlenko 2022-03-30 23:55:34 +01:00
parent 595bc3a2b3
commit 4492a20bbc
No known key found for this signature in database
GPG Key ID: 4C150C250863B96D
4 changed files with 24 additions and 24 deletions

View File

@ -868,7 +868,7 @@ impl Lua {
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))] #[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
pub fn set_hook<F>(&self, triggers: HookTriggers, callback: F) -> Result<()> pub fn set_hook<F>(&self, triggers: HookTriggers, callback: F) -> Result<()>
where where
F: 'static + MaybeSend + FnMut(&Lua, Debug) -> Result<()>, F: 'static + MaybeSend + Fn(&Lua, Debug) -> Result<()>,
{ {
unsafe extern "C" fn hook_proc(state: *mut ffi::lua_State, ar: *mut ffi::lua_Debug) { unsafe extern "C" fn hook_proc(state: *mut ffi::lua_State, ar: *mut ffi::lua_Debug) {
let lua = match Lua::make_from_ptr(state) { let lua = match Lua::make_from_ptr(state) {
@ -880,29 +880,24 @@ impl Lua {
let debug = Debug::new(&lua, ar); let debug = Debug::new(&lua, ar);
let hook_cb = (*lua.extra.get()).hook_callback.clone(); let hook_cb = (*lua.extra.get()).hook_callback.clone();
let hook_cb = mlua_expect!(hook_cb, "no hook callback set in hook_proc"); let hook_cb = mlua_expect!(hook_cb, "no hook callback set in hook_proc");
if Arc::strong_count(&hook_cb) > 2 {
#[allow(clippy::match_wild_err_arm)] return Ok(()); // Don't allow recursion
match hook_cb.try_lock() {
Ok(mut cb) => cb(&lua, debug),
Err(_) => {
mlua_panic!("Lua should not allow hooks to be called within another hook")
} }
}?; hook_cb(&lua, debug)
Ok(())
}) })
} }
let state = self.main_state.ok_or(Error::MainThreadNotAvailable)?; let state = self.main_state.ok_or(Error::MainThreadNotAvailable)?;
unsafe { unsafe {
(*self.extra.get()).hook_callback = Some(Arc::new(Mutex::new(callback))); (*self.extra.get()).hook_callback = Some(Arc::new(callback));
ffi::lua_sethook(state, Some(hook_proc), triggers.mask(), triggers.count()); ffi::lua_sethook(state, Some(hook_proc), triggers.mask(), triggers.count());
} }
Ok(()) Ok(())
} }
/// Remove any hook previously set by `set_hook`. This function has no effect if a hook was not /// Removes any hook previously set by `set_hook`.
/// previously set. ///
/// This function has no effect if a hook was not previously set.
#[cfg(not(feature = "luau"))] #[cfg(not(feature = "luau"))]
#[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))] #[cfg_attr(docsrs, doc(cfg(not(feature = "luau"))))]
pub fn remove_hook(&self) { pub fn remove_hook(&self) {

View File

@ -15,6 +15,10 @@ pub use crate::{
Value as LuaValue, Value as LuaValue,
}; };
#[cfg(not(feature = "luau"))]
#[doc(no_inline)]
pub use crate::HookTriggers as LuaHookTriggers;
#[cfg(feature = "luau")] #[cfg(feature = "luau")]
#[doc(no_inline)] #[doc(no_inline)]
pub use crate::VmState as LuaVmState; pub use crate::VmState as LuaVmState;

View File

@ -49,13 +49,8 @@ pub(crate) struct AsyncPollUpvalue<'lua> {
pub(crate) lua: Lua, pub(crate) lua: Lua,
pub(crate) fut: LocalBoxFuture<'lua, Result<MultiValue<'lua>>>, pub(crate) fut: LocalBoxFuture<'lua, Result<MultiValue<'lua>>>,
} }
#[cfg(all(feature = "send", not(feature = "luau")))]
pub(crate) type HookCallback = Arc<Mutex<dyn FnMut(&Lua, Debug) -> Result<()> + Send>>;
#[cfg(all(not(feature = "send"), not(feature = "luau")))] /// Type to set next Luau VM action after executing interrupt function.
pub(crate) type HookCallback = Arc<Mutex<dyn FnMut(&Lua, Debug) -> Result<()>>>;
/// Type to set next Lua VM action after executing interrupt function.
#[cfg(any(feature = "luau", doc))] #[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))] #[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
pub enum VmState { pub enum VmState {
@ -63,6 +58,12 @@ pub enum VmState {
Yield, Yield,
} }
#[cfg(all(feature = "send", not(feature = "luau")))]
pub(crate) type HookCallback = Arc<dyn Fn(&Lua, Debug) -> Result<()> + Send>;
#[cfg(all(not(feature = "send"), not(feature = "luau")))]
pub(crate) type HookCallback = Arc<dyn Fn(&Lua, Debug) -> Result<()>>;
#[cfg(all(feature = "luau", feature = "send"))] #[cfg(all(feature = "luau", feature = "send"))]
pub(crate) type InterruptCallback = Arc<dyn Fn(&Lua) -> Result<VmState> + Send>; pub(crate) type InterruptCallback = Arc<dyn Fn(&Lua) -> Result<VmState> + Send>;

View File

@ -3,6 +3,7 @@
use std::cell::RefCell; use std::cell::RefCell;
use std::ops::Deref; use std::ops::Deref;
use std::str; use std::str;
use std::sync::atomic::{AtomicI64, Ordering};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use mlua::{DebugEvent, Error, HookTriggers, Lua, Result, Value}; use mlua::{DebugEvent, Error, HookTriggers, Lua, Result, Value};
@ -128,18 +129,17 @@ fn test_error_within_hook() -> Result<()> {
#[test] #[test]
fn test_limit_execution_instructions() -> Result<()> { fn test_limit_execution_instructions() -> Result<()> {
let lua = Lua::new(); let lua = Lua::new();
let mut max_instructions = 10000;
#[cfg(feature = "luajit")]
// For LuaJIT disable JIT, as compiled code does not trigger hooks // For LuaJIT disable JIT, as compiled code does not trigger hooks
#[cfg(feature = "luajit")]
lua.load("jit.off()").exec()?; lua.load("jit.off()").exec()?;
let max_instructions = AtomicI64::new(10000);
lua.set_hook( lua.set_hook(
HookTriggers::every_nth_instruction(30), HookTriggers::every_nth_instruction(30),
move |_lua, debug| { move |_lua, debug| {
assert_eq!(debug.event(), DebugEvent::Count); assert_eq!(debug.event(), DebugEvent::Count);
max_instructions -= 30; if max_instructions.fetch_sub(30, Ordering::Relaxed) <= 30 {
if max_instructions < 0 {
Err(Error::RuntimeError("time's up".to_string())) Err(Error::RuntimeError("time's up".to_string()))
} else { } else {
Ok(()) Ok(())