Don't store extra data in the lua_State extra space
This commit is contained in:
parent
c4fd7a9faf
commit
ec08a76a41
146
src/lua.rs
146
src/lua.rs
|
@ -29,10 +29,22 @@ use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Va
|
||||||
pub struct Lua {
|
pub struct Lua {
|
||||||
pub(crate) state: *mut ffi::lua_State,
|
pub(crate) state: *mut ffi::lua_State,
|
||||||
main_state: *mut ffi::lua_State,
|
main_state: *mut ffi::lua_State,
|
||||||
|
extra: Arc<RefCell<ExtraData>>,
|
||||||
// Lua has lots of interior mutability, should not be RefUnwindSafe
|
// Lua has lots of interior mutability, should not be RefUnwindSafe
|
||||||
_no_ref_unwind_safe: PhantomData<UnsafeCell<()>>,
|
_no_ref_unwind_safe: PhantomData<UnsafeCell<()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Data associated with the lua_State.
|
||||||
|
struct ExtraData {
|
||||||
|
registered_userdata: HashMap<TypeId, c_int>,
|
||||||
|
registry_unref_list: Arc<Mutex<Option<Vec<c_int>>>>,
|
||||||
|
|
||||||
|
ref_thread: *mut ffi::lua_State,
|
||||||
|
ref_stack_size: c_int,
|
||||||
|
ref_stack_max: c_int,
|
||||||
|
ref_free: Vec<c_int>,
|
||||||
|
}
|
||||||
|
|
||||||
unsafe impl Send for Lua {}
|
unsafe impl Send for Lua {}
|
||||||
|
|
||||||
impl Lua {
|
impl Lua {
|
||||||
|
@ -77,11 +89,12 @@ impl Lua {
|
||||||
protect_lua_closure(main_state, 0, 0, |state| {
|
protect_lua_closure(main_state, 0, 0, |state| {
|
||||||
init_error_registry(state);
|
init_error_registry(state);
|
||||||
|
|
||||||
// Create the function metatable
|
// Create the function metatables and place them in the registry
|
||||||
|
// to prevent them from being garbage collected.
|
||||||
|
|
||||||
ffi::lua_pushlightuserdata(
|
ffi::lua_pushlightuserdata(
|
||||||
state,
|
state,
|
||||||
&FUNCTION_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void,
|
&FUNCTION_CALLBACK_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void,
|
||||||
);
|
);
|
||||||
|
|
||||||
ffi::lua_newtable(state);
|
ffi::lua_newtable(state);
|
||||||
|
@ -96,6 +109,23 @@ impl Lua {
|
||||||
|
|
||||||
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
|
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
|
||||||
|
|
||||||
|
ffi::lua_pushlightuserdata(
|
||||||
|
state,
|
||||||
|
&FUNCTION_EXTRA_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void,
|
||||||
|
);
|
||||||
|
|
||||||
|
ffi::lua_newtable(state);
|
||||||
|
|
||||||
|
ffi::lua_pushstring(state, cstr!("__gc"));
|
||||||
|
ffi::lua_pushcfunction(state, userdata_destructor::<Arc<RefCell<ExtraData>>>);
|
||||||
|
ffi::lua_rawset(state, -3);
|
||||||
|
|
||||||
|
ffi::lua_pushstring(state, cstr!("__metatable"));
|
||||||
|
ffi::lua_pushboolean(state, 0);
|
||||||
|
ffi::lua_rawset(state, -3);
|
||||||
|
|
||||||
|
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
|
||||||
|
|
||||||
// Create ref stack thread and place it in the registry to prevent it from being garbage
|
// Create ref stack thread and place it in the registry to prevent it from being garbage
|
||||||
// collected.
|
// collected.
|
||||||
|
|
||||||
|
@ -106,9 +136,9 @@ impl Lua {
|
||||||
"Error during Lua construction",
|
"Error during Lua construction",
|
||||||
);
|
);
|
||||||
|
|
||||||
// Create ExtraData, and place it in the lua_State "extra space"
|
// Create ExtraData
|
||||||
|
|
||||||
let extra = Box::into_raw(Box::new(ExtraData {
|
let extra = Arc::new(RefCell::new(ExtraData {
|
||||||
registered_userdata: HashMap::new(),
|
registered_userdata: HashMap::new(),
|
||||||
registry_unref_list: Arc::new(Mutex::new(Some(Vec::new()))),
|
registry_unref_list: Arc::new(Mutex::new(Some(Vec::new()))),
|
||||||
ref_thread,
|
ref_thread,
|
||||||
|
@ -124,12 +154,10 @@ impl Lua {
|
||||||
);
|
);
|
||||||
assert_stack(main_state, ffi::LUA_MINSTACK);
|
assert_stack(main_state, ffi::LUA_MINSTACK);
|
||||||
|
|
||||||
// Place pointer to ExtraData in the lua_State "extra space"
|
|
||||||
*(ffi::lua_getextraspace(main_state) as *mut *mut ExtraData) = extra;
|
|
||||||
|
|
||||||
Lua {
|
Lua {
|
||||||
state,
|
state,
|
||||||
main_state: main_state,
|
main_state: main_state,
|
||||||
|
extra: extra,
|
||||||
_no_ref_unwind_safe: PhantomData,
|
_no_ref_unwind_safe: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -685,7 +713,7 @@ impl Lua {
|
||||||
|
|
||||||
Ok(RegistryKey {
|
Ok(RegistryKey {
|
||||||
registry_id,
|
registry_id,
|
||||||
unref_list: (*extra_data(self.main_state)).registry_unref_list.clone(),
|
unref_list: self.extra.borrow().registry_unref_list.clone(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -742,12 +770,7 @@ impl Lua {
|
||||||
/// `Error::MismatchedRegistryKey` if passed a `RegistryKey` that was not created with a
|
/// `Error::MismatchedRegistryKey` if passed a `RegistryKey` that was not created with a
|
||||||
/// matching `Lua` state.
|
/// matching `Lua` state.
|
||||||
pub fn owns_registry_value(&self, key: &RegistryKey) -> bool {
|
pub fn owns_registry_value(&self, key: &RegistryKey) -> bool {
|
||||||
unsafe {
|
Arc::ptr_eq(&key.unref_list, &self.extra.borrow().registry_unref_list)
|
||||||
Arc::ptr_eq(
|
|
||||||
&key.unref_list,
|
|
||||||
&(*extra_data(self.main_state)).registry_unref_list,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Remove any registry values whose `RegistryKey`s have all been dropped.
|
/// Remove any registry values whose `RegistryKey`s have all been dropped.
|
||||||
|
@ -759,7 +782,7 @@ impl Lua {
|
||||||
unsafe {
|
unsafe {
|
||||||
let unref_list = mem::replace(
|
let unref_list = mem::replace(
|
||||||
&mut *mlua_expect!(
|
&mut *mlua_expect!(
|
||||||
(*extra_data(self.main_state)).registry_unref_list.lock(),
|
self.extra.borrow().registry_unref_list.lock(),
|
||||||
"unref list poisoned"
|
"unref list poisoned"
|
||||||
),
|
),
|
||||||
Some(Vec::new()),
|
Some(Vec::new()),
|
||||||
|
@ -883,9 +906,9 @@ impl Lua {
|
||||||
lref.lua.main_state == self.main_state,
|
lref.lua.main_state == self.main_state,
|
||||||
"Lua instance passed Value created from a different main Lua state"
|
"Lua instance passed Value created from a different main Lua state"
|
||||||
);
|
);
|
||||||
let extra = extra_data(self.main_state);
|
let extra = self.extra.borrow();
|
||||||
ffi::lua_pushvalue((*extra).ref_thread, lref.index);
|
ffi::lua_pushvalue(extra.ref_thread, lref.index);
|
||||||
ffi::lua_xmove((*extra).ref_thread, self.state, 1);
|
ffi::lua_xmove(extra.ref_thread, self.state, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pops the topmost element of the stack and stores a reference to it. This pins the object,
|
// Pops the topmost element of the stack and stores a reference to it. This pins the object,
|
||||||
|
@ -898,32 +921,34 @@ impl Lua {
|
||||||
// number of short term references being created, and `RegistryKey` being used for long term
|
// number of short term references being created, and `RegistryKey` being used for long term
|
||||||
// references.
|
// references.
|
||||||
pub(crate) unsafe fn pop_ref<'lua>(&'lua self) -> LuaRef<'lua> {
|
pub(crate) unsafe fn pop_ref<'lua>(&'lua self) -> LuaRef<'lua> {
|
||||||
let extra = extra_data(self.main_state);
|
let mut extra = self.extra.borrow_mut();
|
||||||
ffi::lua_xmove(self.state, (*extra).ref_thread, 1);
|
ffi::lua_xmove(self.state, extra.ref_thread, 1);
|
||||||
let index = ref_stack_pop(extra);
|
let index = ref_stack_pop(&mut extra);
|
||||||
LuaRef { lua: self, index }
|
LuaRef { lua: self, index }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn clone_ref<'lua>(&'lua self, lref: &LuaRef<'lua>) -> LuaRef<'lua> {
|
pub(crate) fn clone_ref<'lua>(&'lua self, lref: &LuaRef<'lua>) -> LuaRef<'lua> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let extra = extra_data(self.main_state);
|
let mut extra = self.extra.borrow_mut();
|
||||||
ffi::lua_pushvalue((*extra).ref_thread, lref.index);
|
ffi::lua_pushvalue(extra.ref_thread, lref.index);
|
||||||
let index = ref_stack_pop(extra);
|
let index = ref_stack_pop(&mut extra);
|
||||||
LuaRef { lua: self, index }
|
LuaRef { lua: self, index }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn drop_ref<'lua>(&'lua self, lref: &mut LuaRef<'lua>) {
|
pub(crate) fn drop_ref<'lua>(&'lua self, lref: &mut LuaRef<'lua>) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let extra = extra_data(self.main_state);
|
let mut extra = self.extra.borrow_mut();
|
||||||
ffi::lua_pushnil((*extra).ref_thread);
|
ffi::lua_pushnil(extra.ref_thread);
|
||||||
ffi::lua_replace((*extra).ref_thread, lref.index);
|
ffi::lua_replace(extra.ref_thread, lref.index);
|
||||||
(*extra).ref_free.push(lref.index);
|
extra.ref_free.push(lref.index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) unsafe fn userdata_metatable<T: 'static + UserData>(&self) -> Result<c_int> {
|
pub(crate) unsafe fn userdata_metatable<T: 'static + UserData>(&self) -> Result<c_int> {
|
||||||
if let Some(table_id) = (*extra_data(self.main_state))
|
if let Some(table_id) = self
|
||||||
|
.extra
|
||||||
|
.borrow()
|
||||||
.registered_userdata
|
.registered_userdata
|
||||||
.get(&TypeId::of::<T>())
|
.get(&TypeId::of::<T>())
|
||||||
{
|
{
|
||||||
|
@ -969,9 +994,12 @@ impl Lua {
|
||||||
let id = protect_lua_closure(self.state, 1, 0, |state| {
|
let id = protect_lua_closure(self.state, 1, 0, |state| {
|
||||||
ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX)
|
ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX)
|
||||||
})?;
|
})?;
|
||||||
(*extra_data(self.main_state))
|
|
||||||
|
self.extra
|
||||||
|
.borrow_mut()
|
||||||
.registered_userdata
|
.registered_userdata
|
||||||
.insert(TypeId::of::<T>(), id);
|
.insert(TypeId::of::<T>(), id);
|
||||||
|
|
||||||
Ok(id)
|
Ok(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -992,14 +1020,21 @@ impl Lua {
|
||||||
if ffi::lua_type(state, ffi::lua_upvalueindex(1)) == ffi::LUA_TNIL {
|
if ffi::lua_type(state, ffi::lua_upvalueindex(1)) == ffi::LUA_TNIL {
|
||||||
return Err(Error::CallbackDestructed);
|
return Err(Error::CallbackDestructed);
|
||||||
}
|
}
|
||||||
|
if ffi::lua_type(state, ffi::lua_upvalueindex(2)) == ffi::LUA_TNIL {
|
||||||
|
return Err(Error::CallbackDestructed);
|
||||||
|
}
|
||||||
|
|
||||||
if nargs < ffi::LUA_MINSTACK {
|
if nargs < ffi::LUA_MINSTACK {
|
||||||
check_stack(state, ffi::LUA_MINSTACK - nargs)?;
|
check_stack(state, ffi::LUA_MINSTACK - nargs)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let extra =
|
||||||
|
get_userdata::<Arc<RefCell<ExtraData>>>(state, ffi::lua_upvalueindex(2));
|
||||||
|
|
||||||
let lua = Lua {
|
let lua = Lua {
|
||||||
state: state,
|
state: state,
|
||||||
main_state: get_main_state(state),
|
main_state: get_main_state(state),
|
||||||
|
extra: (*extra).clone(),
|
||||||
_no_ref_unwind_safe: PhantomData,
|
_no_ref_unwind_safe: PhantomData,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1025,19 +1060,26 @@ impl Lua {
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let _sg = StackGuard::new(self.state);
|
let _sg = StackGuard::new(self.state);
|
||||||
assert_stack(self.state, 4);
|
assert_stack(self.state, 6);
|
||||||
|
|
||||||
push_userdata::<Callback>(self.state, func)?;
|
push_userdata::<Callback>(self.state, func)?;
|
||||||
|
|
||||||
ffi::lua_pushlightuserdata(
|
ffi::lua_pushlightuserdata(
|
||||||
self.state,
|
self.state,
|
||||||
&FUNCTION_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void,
|
&FUNCTION_CALLBACK_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void,
|
||||||
);
|
);
|
||||||
ffi::lua_rawget(self.state, ffi::LUA_REGISTRYINDEX);
|
ffi::lua_rawget(self.state, ffi::LUA_REGISTRYINDEX);
|
||||||
ffi::lua_setmetatable(self.state, -2);
|
ffi::lua_setmetatable(self.state, -2);
|
||||||
|
|
||||||
protect_lua_closure(self.state, 1, 1, |state| {
|
push_userdata::<Arc<RefCell<ExtraData>>>(self.state, self.extra.clone())?;
|
||||||
ffi::lua_pushcclosure(state, call_callback, 1);
|
ffi::lua_pushlightuserdata(
|
||||||
|
self.state,
|
||||||
|
&FUNCTION_EXTRA_METATABLE_REGISTRY_KEY as *const u8 as *mut c_void,
|
||||||
|
);
|
||||||
|
ffi::lua_rawget(self.state, ffi::LUA_REGISTRYINDEX);
|
||||||
|
ffi::lua_setmetatable(self.state, -2);
|
||||||
|
|
||||||
|
protect_lua_closure(self.state, 2, 1, |state| {
|
||||||
|
ffi::lua_pushcclosure(state, call_callback, 2);
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(Function(self.pop_ref()))
|
Ok(Function(self.pop_ref()))
|
||||||
|
@ -1066,21 +1108,6 @@ impl Lua {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Data associated with the main lua_State via lua_getextraspace.
|
|
||||||
struct ExtraData {
|
|
||||||
registered_userdata: HashMap<TypeId, c_int>,
|
|
||||||
registry_unref_list: Arc<Mutex<Option<Vec<c_int>>>>,
|
|
||||||
|
|
||||||
ref_thread: *mut ffi::lua_State,
|
|
||||||
ref_stack_size: c_int,
|
|
||||||
ref_stack_max: c_int,
|
|
||||||
ref_free: Vec<c_int>,
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn extra_data(state: *mut ffi::lua_State) -> *mut ExtraData {
|
|
||||||
*(ffi::lua_getextraspace(state) as *mut *mut ExtraData)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returned from [`Lua::load`] and is used to finalize loading and executing Lua main chunks.
|
/// Returned from [`Lua::load`] and is used to finalize loading and executing Lua main chunks.
|
||||||
///
|
///
|
||||||
/// [`Lua::load`]: struct.Lua.html#method.load
|
/// [`Lua::load`]: struct.Lua.html#method.load
|
||||||
|
@ -1166,25 +1193,26 @@ impl<'lua, 'a> Chunk<'lua, 'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn ref_stack_pop(extra: *mut ExtraData) -> c_int {
|
unsafe fn ref_stack_pop(extra: &mut ExtraData) -> c_int {
|
||||||
if let Some(free) = (*extra).ref_free.pop() {
|
if let Some(free) = extra.ref_free.pop() {
|
||||||
ffi::lua_replace((*extra).ref_thread, free);
|
ffi::lua_replace(extra.ref_thread, free);
|
||||||
free
|
free
|
||||||
} else {
|
} else {
|
||||||
if (*extra).ref_stack_max >= (*extra).ref_stack_size {
|
if extra.ref_stack_max >= extra.ref_stack_size {
|
||||||
// It is a user error to create enough references to exhaust the Lua max stack size for
|
// It is a user error to create enough references to exhaust the Lua max stack size for
|
||||||
// the ref thread.
|
// the ref thread.
|
||||||
if ffi::lua_checkstack((*extra).ref_thread, (*extra).ref_stack_size) == 0 {
|
if ffi::lua_checkstack(extra.ref_thread, extra.ref_stack_size) == 0 {
|
||||||
mlua_panic!("cannot create a Lua reference, out of auxiliary stack space");
|
mlua_panic!("cannot create a Lua reference, out of auxiliary stack space");
|
||||||
}
|
}
|
||||||
(*extra).ref_stack_size *= 2;
|
extra.ref_stack_size *= 2;
|
||||||
}
|
}
|
||||||
(*extra).ref_stack_max += 1;
|
extra.ref_stack_max += 1;
|
||||||
(*extra).ref_stack_max
|
extra.ref_stack_max
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static FUNCTION_METATABLE_REGISTRY_KEY: u8 = 0;
|
static FUNCTION_CALLBACK_METATABLE_REGISTRY_KEY: u8 = 0;
|
||||||
|
static FUNCTION_EXTRA_METATABLE_REGISTRY_KEY: u8 = 0;
|
||||||
|
|
||||||
struct StaticUserDataMethods<'lua, T: 'static + UserData> {
|
struct StaticUserDataMethods<'lua, T: 'static + UserData> {
|
||||||
methods: Vec<(Vec<u8>, Callback<'lua, 'static>)>,
|
methods: Vec<(Vec<u8>, Callback<'lua, 'static>)>,
|
||||||
|
|
Loading…
Reference in New Issue