From 059e41bafba68aa06d7c6b4079d8d67f1ac4ca0b Mon Sep 17 00:00:00 2001 From: Alex Orlenko Date: Sun, 17 Jul 2022 10:57:36 +0100 Subject: [PATCH] Optimize `WrappedFailure` userdata detection. This is done by comparing a metatable pointer to previously saved one (it never changes). --- src/lua.rs | 17 ++++++++++++++++- src/util.rs | 36 ++++++++++++++++++++++++------------ 2 files changed, 40 insertions(+), 13 deletions(-) diff --git a/src/lua.rs b/src/lua.rs index 1a20328..73bc1db 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -111,6 +111,9 @@ pub(crate) struct ExtraData { #[cfg(feature = "async")] recycled_thread_cache: Vec, + // Address of `WrappedFailure` metatable + wrapped_failure_mt_ptr: *const c_void, + // Index of `Option` userdata on the ref thread #[cfg(feature = "async")] ref_waker_idx: c_int, @@ -538,6 +541,13 @@ impl Lua { "Error while creating ref thread", ); + let wrapped_failure_mt_ptr = { + get_gc_metatable::(state); + let ptr = ffi::lua_topointer(state, -1); + ffi::lua_pop(state, 1); + ptr + }; + // Create empty Waker slot on the ref thread #[cfg(feature = "async")] let ref_waker_idx = { @@ -568,6 +578,7 @@ impl Lua { multivalue_cache: Vec::with_capacity(MULTIVALUE_CACHE_SIZE), #[cfg(feature = "async")] recycled_thread_cache: Vec::new(), + wrapped_failure_mt_ptr, #[cfg(feature = "async")] ref_waker_idx, #[cfg(not(feature = "luau"))] @@ -2293,6 +2304,8 @@ impl Lua { // Uses 2 stack spaces, does not call checkstack pub(crate) unsafe fn pop_value(&self) -> Value { let state = self.state; + let extra = &mut *self.extra.get(); + match ffi::lua_type(state, -1) { ffi::LUA_TNIL => { ffi::lua_pop(state, 1); @@ -2353,9 +2366,11 @@ impl Lua { ffi::LUA_TFUNCTION => Value::Function(Function(self.pop_ref())), ffi::LUA_TUSERDATA => { + let wrapped_failure_mt_ptr = extra.wrapped_failure_mt_ptr; // We must prevent interaction with userdata types other than UserData OR a WrappedError. // WrappedPanics are automatically resumed. - match get_gc_userdata::(state, -1).as_mut() { + match get_gc_userdata::(state, -1, wrapped_failure_mt_ptr).as_mut() + { Some(WrappedFailure::Error(err)) => { let err = err.clone(); ffi::lua_pop(state, 1); diff --git a/src/util.rs b/src/util.rs index aca50f1..592f54c 100644 --- a/src/util.rs +++ b/src/util.rs @@ -203,7 +203,7 @@ pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error { "pop_error called with non-error return code" ); - match get_gc_userdata::(state, -1).as_mut() { + match get_gc_userdata::(state, -1, ptr::null()).as_mut() { Some(WrappedFailure::Error(err)) => { ffi::lua_pop(state, 1); err.clone() @@ -394,16 +394,28 @@ pub unsafe fn push_gc_userdata( } // Uses 2 stack spaces, does not call checkstack -pub unsafe fn get_gc_userdata(state: *mut ffi::lua_State, index: c_int) -> *mut T { +pub unsafe fn get_gc_userdata( + state: *mut ffi::lua_State, + index: c_int, + mt_ptr: *const c_void, +) -> *mut T { let ud = ffi::lua_touserdata(state, index) as *mut T; if ud.is_null() || ffi::lua_getmetatable(state, index) == 0 { return ptr::null_mut(); } - get_gc_metatable::(state); - let res = ffi::lua_rawequal(state, -1, -2); - ffi::lua_pop(state, 2); - if res == 0 { - return ptr::null_mut(); + if !mt_ptr.is_null() { + let ud_mt_ptr = ffi::lua_topointer(state, -1); + ffi::lua_pop(state, 1); + if !ptr::eq(ud_mt_ptr, mt_ptr) { + return ptr::null_mut(); + } + } else { + get_gc_metatable::(state); + let res = ffi::lua_rawequal(state, -1, -2); + ffi::lua_pop(state, 2); + if res == 0 { + return ptr::null_mut(); + } } ud } @@ -679,7 +691,7 @@ pub unsafe extern "C" fn error_traceback(state: *mut ffi::lua_State) -> c_int { return 1; } - if get_gc_userdata::(state, -1).is_null() { + if get_gc_userdata::(state, -1, ptr::null()).is_null() { let s = ffi::luaL_tolstring(state, -1, ptr::null_mut()); if ffi::lua_checkstack(state, ffi::LUA_TRACEBACK_STACK) != 0 { ffi::luaL_traceback(state, state, s, 0); @@ -706,7 +718,7 @@ pub unsafe extern "C" fn safe_pcall(state: *mut ffi::lua_State) -> c_int { ffi::lua_gettop(state) } else { if let Some(WrappedFailure::Panic(_)) = - get_gc_userdata::(state, -1).as_ref() + get_gc_userdata::(state, -1, ptr::null()).as_ref() { ffi::lua_error(state); } @@ -722,7 +734,7 @@ pub unsafe extern "C" fn safe_xpcall(state: *mut ffi::lua_State) -> c_int { ffi::luaL_checkstack(state, 2, ptr::null()); if let Some(WrappedFailure::Panic(_)) = - get_gc_userdata::(state, -1).as_ref() + get_gc_userdata::(state, -1, ptr::null()).as_ref() { 1 } else { @@ -752,7 +764,7 @@ pub unsafe extern "C" fn safe_xpcall(state: *mut ffi::lua_State) -> c_int { ffi::lua_gettop(state) - 1 } else { if let Some(WrappedFailure::Panic(_)) = - get_gc_userdata::(state, -1).as_ref() + get_gc_userdata::(state, -1, ptr::null()).as_ref() { ffi::lua_error(state); } @@ -836,7 +848,7 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> { callback_error(state, |_| { check_stack(state, 3)?; - let err_buf = match get_gc_userdata::(state, -1).as_ref() { + let err_buf = match get_gc_userdata::(state, -1, ptr::null()).as_ref() { Some(WrappedFailure::Error(error)) => { let err_buf_key = &ERROR_PRINT_BUFFER_KEY as *const u8 as *const c_void; ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, err_buf_key);