From adbc9ccc9be5d24ff56275938aebb11529ec7399 Mon Sep 17 00:00:00 2001 From: Alex Orlenko Date: Thu, 8 Jul 2021 15:37:18 +0100 Subject: [PATCH] Move away from metatable hashmap cache to direct keys --- src/lua.rs | 73 +++++++++++++++++++++++------------- src/thread.rs | 6 +-- src/util.rs | 101 ++++++++++++++++++++++---------------------------- 3 files changed, 96 insertions(+), 84 deletions(-) diff --git a/src/lua.rs b/src/lua.rs index a0c094e..af38bb1 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -27,10 +27,10 @@ use crate::userdata::{ }; use crate::util::{ self, assert_stack, callback_error, check_stack, get_destructed_userdata_metatable, - get_gc_metatable_for, get_gc_userdata, get_main_state, get_userdata, get_wrapped_error, - init_error_registry, init_gc_metatable_for, init_userdata_metatable, pop_error, - push_gc_userdata, push_string, push_table, push_userdata, push_wrapped_error, rawset_field, - safe_pcall, safe_xpcall, StackGuard, WrappedError, WrappedPanic, + get_gc_metatable, get_gc_userdata, get_main_state, get_userdata, get_wrapped_error, + init_error_registry, init_gc_metatable, init_userdata_metatable, pop_error, push_gc_userdata, + push_string, push_table, push_userdata, push_wrapped_error, rawset_field, safe_pcall, + safe_xpcall, StackGuard, WrappedError, WrappedPanic, WRAPPED_ERROR_MT, WRAPPED_PANIC_MT, }; use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value}; @@ -148,11 +148,24 @@ impl LuaOptions { } } +static CALLBACK_MT: u8 = 0; +static CALLBACK_UPVALUE_MT: u8 = 0; + +static EXTRA_MT: u8 = 0; +pub(crate) static EXTRA_REGISTRY_KEY: u8 = 0; + #[cfg(feature = "async")] -pub(crate) static ASYNC_POLL_PENDING: u8 = 0; +static ASYNC_CALLBACK_MT: u8 = 0; +#[cfg(feature = "async")] +static ASYNC_CALLBACK_UPVALUE_MT: u8 = 0; +#[cfg(feature = "async")] +static ASYNC_POLL_UPVALUE_MT: u8 = 0; +#[cfg(feature = "async")] +pub(crate) static WAKER_MT: u8 = 0; #[cfg(feature = "async")] pub(crate) static WAKER_REGISTRY_KEY: u8 = 0; -pub(crate) static EXTRA_REGISTRY_KEY: u8 = 0; +#[cfg(feature = "async")] +pub(crate) static ASYNC_POLL_PENDING: u8 = 0; /// Requires `feature = "send"` #[cfg(feature = "send")] @@ -396,18 +409,19 @@ impl Lua { // Create the internal metatables and place them in the registry // to prevent them from being garbage collected. - init_gc_metatable_for::(state, None)?; - init_gc_metatable_for::(state, None)?; - init_gc_metatable_for::>>(state, None)?; + init_gc_metatable::(state, &CALLBACK_MT, None)?; + init_gc_metatable::(state, &CALLBACK_UPVALUE_MT, None)?; + init_gc_metatable::>>(state, &EXTRA_MT, None)?; #[cfg(feature = "async")] { - init_gc_metatable_for::(state, None)?; - init_gc_metatable_for::(state, None)?; - init_gc_metatable_for::(state, None)?; - init_gc_metatable_for::>(state, None)?; + init_gc_metatable::(state, &ASYNC_CALLBACK_MT, None)?; + #[rustfmt::skip] + init_gc_metatable::(state, &ASYNC_CALLBACK_UPVALUE_MT, None)?; + init_gc_metatable::(state, &ASYNC_POLL_UPVALUE_MT, None)?; + init_gc_metatable::>(state, &WAKER_MT, None)?; // Create empty Waker slot - push_gc_userdata::>(state, None)?; + push_gc_userdata::>(state, &WAKER_MT, None)?; protect_lua!(state, 1, 0, state => { let waker_key = &WAKER_REGISTRY_KEY as *const u8 as *const c_void; ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, waker_key); @@ -450,7 +464,7 @@ impl Lua { })); mlua_expect!( - push_gc_userdata(main_state, Arc::downgrade(&extra)), + push_gc_userdata(main_state, &EXTRA_MT, Arc::downgrade(&extra)), "Error while storing extra data", ); mlua_expect!( @@ -1612,7 +1626,9 @@ impl Lua { let err = err.clone(); ffi::lua_pop(state, 1); Value::Error(err) - } else if let Some(panic) = get_gc_userdata::(state, -1).as_mut() { + } else if let Some(panic) = + get_gc_userdata::(state, -1, &WRAPPED_PANIC_MT).as_mut() + { if let Some(panic) = (*panic).0.take() { ffi::lua_pop(state, 1); resume_unwind(panic); @@ -1877,8 +1893,12 @@ impl Lua { check_stack(self.state, 4)?; let lua = self.clone(); - let func = mem::transmute(func); - push_gc_userdata(self.state, CallbackUpvalue { lua, func })?; + push_gc_userdata( + self.state, + &CALLBACK_UPVALUE_MT, + CallbackUpvalue { lua, func }, + )?; + protect_lua!(self.state, 1, 1, state => { ffi::lua_pushcclosure(state, call_callback, 1); })?; @@ -1930,7 +1950,7 @@ impl Lua { let fut = ((*upvalue).func)(lua, args); let lua = lua.clone(); - push_gc_userdata(state, AsyncPollUpvalue { lua, fut })?; + push_gc_userdata(state, &ASYNC_POLL_UPVALUE_MT, AsyncPollUpvalue { lua, fut })?; protect_lua!(state, 1, 1, state => { ffi::lua_pushcclosure(state, poll_future, 1); })?; @@ -1961,7 +1981,7 @@ impl Lua { // Try to get an outer poll waker let waker_key = &WAKER_REGISTRY_KEY as *const u8 as *const c_void; ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, waker_key); - let waker = match get_gc_userdata::>(state, -1).as_ref() { + let waker = match get_gc_userdata::>(state, -1, &WAKER_MT).as_ref() { Some(Some(waker)) => waker.clone(), _ => noop_waker(), }; @@ -1995,8 +2015,11 @@ impl Lua { check_stack(self.state, 4)?; let lua = self.clone(); - let func = mem::transmute(func); - push_gc_userdata(self.state, AsyncCallbackUpvalue { lua, func })?; + push_gc_userdata( + self.state, + &ASYNC_CALLBACK_UPVALUE_MT, + AsyncCallbackUpvalue { lua, func }, + )?; protect_lua!(self.state, 1, 1, state => { ffi::lua_pushcclosure(state, call_callback, 1); })?; @@ -2104,7 +2127,7 @@ impl Lua { return None; } let extra = mlua_expect!( - (*get_gc_userdata::>>(state, -1)).upgrade(), + (*get_gc_userdata::>>(state, -1, &EXTRA_MT)).upgrade(), "extra is destroyed" ); ffi::lua_pop(state, 1); @@ -2437,7 +2460,7 @@ where Ok(Err(err)) => { let wrapped_error = get_prealloc_err() as *mut WrappedError; ptr::write(wrapped_error, WrappedError(err)); - get_gc_metatable_for::(state); + get_gc_metatable(state, &WRAPPED_ERROR_MT); ffi::lua_setmetatable(state, -2); // Convert to CallbackError and attach traceback @@ -2457,7 +2480,7 @@ where Err(p) => { let wrapped_panic = get_prealloc_err() as *mut WrappedPanic; ptr::write(wrapped_panic, WrappedPanic(Some(p))); - get_gc_metatable_for::(state); + get_gc_metatable(state, &WRAPPED_PANIC_MT); ffi::lua_setmetatable(state, -2); ffi::lua_error(state) } diff --git a/src/thread.rs b/src/thread.rs index 403eb78..7a10d46 100644 --- a/src/thread.rs +++ b/src/thread.rs @@ -13,7 +13,7 @@ use crate::function::Function; #[cfg(feature = "async")] use { crate::{ - lua::{ASYNC_POLL_PENDING, WAKER_REGISTRY_KEY}, + lua::{ASYNC_POLL_PENDING, WAKER_MT, WAKER_REGISTRY_KEY}, util::get_gc_userdata, value::Value, }, @@ -362,7 +362,7 @@ impl WakerGuard { let waker_key = &WAKER_REGISTRY_KEY as *const u8 as *const c_void; ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, waker_key); - let waker_slot = get_gc_userdata::>(state, -1).as_mut(); + let waker_slot = get_gc_userdata::>(state, -1, &WAKER_MT).as_mut(); let old = mlua_expect!(waker_slot, "Waker is destroyed").replace(waker); Ok(WakerGuard(state, old)) @@ -380,7 +380,7 @@ impl Drop for WakerGuard { let waker_key = &WAKER_REGISTRY_KEY as *const u8 as *const c_void; ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, waker_key); - let waker_slot = get_gc_userdata::>(state, -1).as_mut(); + let waker_slot = get_gc_userdata::>(state, -1, &WAKER_MT).as_mut(); mem::swap(mlua_expect!(waker_slot, "Waker is destroyed"), &mut self.1); } } diff --git a/src/util.rs b/src/util.rs index b55b109..244b8da 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,21 +1,18 @@ -use std::any::{Any, TypeId}; -use std::collections::HashMap; +use std::any::Any; use std::error::Error as StdError; use std::fmt::Write; use std::os::raw::{c_char, c_int, c_void}; use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe}; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use std::{mem, ptr, slice}; -use once_cell::sync::Lazy; - use crate::error::{Error, Result}; use crate::ffi; -static METATABLE_CACHE: Lazy>> = Lazy::new(|| { - // The capacity must(!) be greater than number of stored keys - Mutex::new(HashMap::with_capacity(32)) -}); +pub(crate) static WRAPPED_ERROR_MT: u8 = 0; +pub(crate) static WRAPPED_PANIC_MT: u8 = 0; +static DESTRUCTED_USERDATA_MT: u8 = 0; +static ERROR_PRINT_BUFFER_KEY: u8 = 0; // Checks that Lua has enough free stack space for future stack operations. On failure, this will // panic with an internal error message. @@ -199,7 +196,9 @@ pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error { if let Some(err) = get_wrapped_error(state, -1).as_ref() { ffi::lua_pop(state, 1); err.clone() - } else if let Some(panic) = get_gc_userdata::(state, -1).as_mut() { + } else if let Some(panic) = + get_gc_userdata::(state, -1, &WRAPPED_PANIC_MT).as_mut() + { if let Some(p) = (*panic).0.take() { resume_unwind(p); } else { @@ -298,20 +297,24 @@ pub unsafe fn take_userdata(state: *mut ffi::lua_State) -> T { // Pushes the userdata and attaches a metatable with __gc method. // Internally uses 3 stack spaces, does not call checkstack. -pub unsafe fn push_gc_userdata(state: *mut ffi::lua_State, t: T) -> Result<()> { +pub unsafe fn push_gc_userdata(state: *mut ffi::lua_State, key: *const u8, t: T) -> Result<()> { push_userdata(state, t)?; - get_gc_metatable_for::(state); + get_gc_metatable(state, key); ffi::lua_setmetatable(state, -2); Ok(()) } // 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, + key: *const u8, +) -> *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_for::(state); + get_gc_metatable(state, key); let res = ffi::lua_rawequal(state, -1, -2); ffi::lua_pop(state, 2); if res == 0 { @@ -525,7 +528,7 @@ where let wrapped_error = ud as *mut WrappedError; ptr::write(wrapped_error, WrappedError(err)); - get_gc_metatable_for::(state); + get_gc_metatable(state, &WRAPPED_ERROR_MT); ffi::lua_setmetatable(state, -2); // Convert to CallbackError and attach traceback @@ -545,7 +548,7 @@ where Err(p) => { ffi::lua_settop(state, 1); ptr::write(ud as *mut WrappedPanic, WrappedPanic(Some(p))); - get_gc_metatable_for::(state); + get_gc_metatable(state, &WRAPPED_PANIC_MT); ffi::lua_setmetatable(state, -2); ffi::lua_error(state) } @@ -559,8 +562,8 @@ pub unsafe extern "C" fn error_traceback(state: *mut ffi::lua_State) -> c_int { return 1; } - if get_gc_userdata::(state, -1).is_null() - && get_gc_userdata::(state, -1).is_null() + if get_gc_userdata::(state, -1, &WRAPPED_ERROR_MT).is_null() + && get_gc_userdata::(state, -1, &WRAPPED_PANIC_MT).is_null() { let s = ffi::luaL_tolstring(state, -1, ptr::null_mut()); if ffi::lua_checkstack(state, ffi::LUA_TRACEBACK_STACK) != 0 { @@ -587,7 +590,7 @@ pub unsafe extern "C" fn safe_pcall(state: *mut ffi::lua_State) -> c_int { ffi::lua_insert(state, 1); ffi::lua_gettop(state) } else { - if !get_gc_userdata::(state, -1).is_null() { + if !get_gc_userdata::(state, -1, &WRAPPED_PANIC_MT).is_null() { ffi::lua_error(state); } ffi::lua_pushboolean(state, 0); @@ -601,7 +604,7 @@ pub unsafe extern "C" fn safe_xpcall(state: *mut ffi::lua_State) -> c_int { unsafe extern "C" fn xpcall_msgh(state: *mut ffi::lua_State) -> c_int { ffi::luaL_checkstack(state, 2, ptr::null()); - if !get_gc_userdata::(state, -1).is_null() { + if !get_gc_userdata::(state, -1, &WRAPPED_PANIC_MT).is_null() { 1 } else { ffi::lua_pushvalue(state, ffi::lua_upvalueindex(1)); @@ -629,7 +632,7 @@ pub unsafe extern "C" fn safe_xpcall(state: *mut ffi::lua_State) -> c_int { ffi::lua_insert(state, 2); ffi::lua_gettop(state) - 1 } else { - if !get_gc_userdata::(state, -1).is_null() { + if !get_gc_userdata::(state, -1, &WRAPPED_PANIC_MT).is_null() { ffi::lua_error(state); } ffi::lua_pushboolean(state, 0); @@ -664,14 +667,14 @@ pub unsafe fn get_main_state(state: *mut ffi::lua_State) -> Option<*mut ffi::lua // Pushes a WrappedError to the top of the stack. // Uses 3 stack spaces and does not call checkstack. pub unsafe fn push_wrapped_error(state: *mut ffi::lua_State, err: Error) -> Result<()> { - push_gc_userdata::(state, WrappedError(err)) + push_gc_userdata::(state, &WRAPPED_ERROR_MT, WrappedError(err)) } // Checks if the value at the given index is a WrappedError, and if it is returns a pointer to it, // otherwise returns null. // Uses 2 stack spaces and does not call checkstack. pub unsafe fn get_wrapped_error(state: *mut ffi::lua_State, index: c_int) -> *const Error { - let ud = get_gc_userdata::(state, index); + let ud = get_gc_userdata::(state, index, &WRAPPED_ERROR_MT); if ud.is_null() { return ptr::null(); } @@ -680,23 +683,13 @@ pub unsafe fn get_wrapped_error(state: *mut ffi::lua_State, index: c_int) -> *co // Initialize the internal (with __gc method) metatable for a type T. // Uses 6 stack spaces and calls checkstack. -pub unsafe fn init_gc_metatable_for( +pub unsafe fn init_gc_metatable( state: *mut ffi::lua_State, + key: *const u8, customize_fn: Option Result<()>>, ) -> Result<()> { check_stack(state, 6)?; - let type_id = TypeId::of::(); - let ref_addr = { - let mut mt_cache = mlua_expect!(METATABLE_CACHE.lock(), "cannot lock metatable cache"); - mlua_assert!( - mt_cache.capacity() - mt_cache.len() > 0, - "out of metatable cache capacity" - ); - mt_cache.insert(type_id, 0); - &mt_cache[&type_id] as *const u8 - }; - push_table(state, 0, 3)?; ffi::lua_pushcfunction(state, userdata_destructor::); @@ -710,19 +703,14 @@ pub unsafe fn init_gc_metatable_for( } protect_lua!(state, 1, 0, |state| { - ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, ref_addr as *mut c_void) + ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, key as *const c_void) })?; Ok(()) } -pub unsafe fn get_gc_metatable_for(state: *mut ffi::lua_State) { - let type_id = TypeId::of::(); - let ref_addr = { - let mt_cache = mlua_expect!(METATABLE_CACHE.lock(), "cannot lock metatable cache"); - mlua_expect!(mt_cache.get(&type_id), "gc metatable does not exist") as *const u8 - }; - ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, ref_addr as *const c_void); +pub unsafe fn get_gc_metatable(state: *mut ffi::lua_State, key: *const u8) { + ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, key as *const c_void); } // Initialize the error, panic, and destructed userdata metatables. @@ -771,7 +759,9 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> { _ => {} } Ok(err_buf) - } else if let Some(panic) = get_gc_userdata::(state, -1).as_ref() { + } else if let Some(panic) = + get_gc_userdata::(state, -1, &WRAPPED_PANIC_MT).as_ref() + { if let Some(ref p) = (*panic).0 { 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); @@ -802,16 +792,18 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> { }) } - init_gc_metatable_for::( + init_gc_metatable::( state, + &WRAPPED_ERROR_MT, Some(|state| { ffi::lua_pushcfunction(state, error_tostring); rawset_field(state, -2, "__tostring") }), )?; - init_gc_metatable_for::( + init_gc_metatable::( state, + &WRAPPED_PANIC_MT, Some(|state| { ffi::lua_pushcfunction(state, error_tostring); rawset_field(state, -2, "__tostring") @@ -870,16 +862,16 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> { ffi::lua_pop(state, 1); protect_lua!(state, 1, 0, state => { - let destructed_metatable_key = &DESTRUCTED_USERDATA_METATABLE as *const u8 as *const c_void; - ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, destructed_metatable_key) + let key = &DESTRUCTED_USERDATA_MT as *const u8 as *const c_void; + ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, key) })?; // Create error print buffer - init_gc_metatable_for::(state, None)?; - push_gc_userdata(state, String::new())?; + init_gc_metatable::(state, &ERROR_PRINT_BUFFER_KEY, None)?; + push_gc_userdata(state, &ERROR_PRINT_BUFFER_KEY, String::new())?; protect_lua!(state, 1, 0, state => { - let err_buf_key = &ERROR_PRINT_BUFFER_KEY as *const u8 as *const c_void; - ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, err_buf_key) + let key = &ERROR_PRINT_BUFFER_KEY as *const u8 as *const c_void; + ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, key) })?; Ok(()) @@ -923,9 +915,6 @@ pub(crate) unsafe fn to_string(state: *mut ffi::lua_State, index: c_int) -> Stri } pub(crate) unsafe fn get_destructed_userdata_metatable(state: *mut ffi::lua_State) { - let key = &DESTRUCTED_USERDATA_METATABLE as *const u8 as *const c_void; + let key = &DESTRUCTED_USERDATA_MT as *const u8 as *const c_void; ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, key); } - -static DESTRUCTED_USERDATA_METATABLE: u8 = 0; -static ERROR_PRINT_BUFFER_KEY: u8 = 0;