More performance optimization (userdata part)

This commit is contained in:
Alex Orlenko 2022-06-06 21:42:55 +01:00
parent f9f32bffce
commit 93d36b9068
No known key found for this signature in database
GPG Key ID: 4C150C250863B96D
4 changed files with 124 additions and 99 deletions

View File

@ -538,7 +538,7 @@ impl Lua {
#[cfg(feature = "async")]
let ref_waker_idx = {
mlua_expect!(
push_gc_userdata::<Option<Waker>>(ref_thread, None),
push_gc_userdata::<Option<Waker>>(ref_thread, None, true),
"Error while creating Waker slot"
);
ffi::lua_gettop(ref_thread)
@ -578,7 +578,7 @@ impl Lua {
mlua_expect!(
(|state| {
push_gc_userdata(state, Arc::clone(&extra))?;
push_gc_userdata(state, Arc::clone(&extra), true)?;
protect_lua!(state, 1, 0, fn(state) {
let extra_key = &EXTRA_REGISTRY_KEY as *const u8 as *const c_void;
ffi::lua_rawsetp(state, ffi::LUA_REGISTRYINDEX, extra_key);
@ -1408,29 +1408,15 @@ impl Lua {
let _sg = StackGuard::new(self.state);
check_stack(self.state, 3)?;
if self.unlikely_memory_error() {
let s = s.as_ref();
ffi::lua_pushlstring(self.state, s.as_ptr() as *const c_char, s.len());
} else {
push_string(self.state, s)?;
}
let protect = !self.unlikely_memory_error();
push_string(self.state, s, protect)?;
Ok(String(self.pop_ref()))
}
}
/// Creates and returns a new empty table.
pub fn create_table(&self) -> Result<Table> {
unsafe {
let _sg = StackGuard::new(self.state);
check_stack(self.state, 2)?;
if self.unlikely_memory_error() {
ffi::lua_newtable(self.state);
} else {
protect_lua!(self.state, 0, 1, fn(state) ffi::lua_newtable(state))?;
}
Ok(Table(self.pop_ref()))
}
self.create_table_with_capacity(0, 0)
}
/// Creates and returns a new empty table, with the specified capacity.
@ -1442,12 +1428,8 @@ impl Lua {
let _sg = StackGuard::new(self.state);
check_stack(self.state, 3)?;
if self.unlikely_memory_error() {
ffi::lua_createtable(self.state, narr, nrec);
} else {
push_table(self.state, narr, nrec)?;
}
let protect = !self.unlikely_memory_error();
push_table(self.state, narr, nrec, protect)?;
Ok(Table(self.pop_ref()))
}
}
@ -1465,20 +1447,15 @@ impl Lua {
let iter = iter.into_iter();
let lower_bound = iter.size_hint().0;
if self.unlikely_memory_error() {
ffi::lua_createtable(self.state, 0, lower_bound as c_int);
for (k, v) in iter {
self.push_value(k.to_lua(self)?)?;
self.push_value(v.to_lua(self)?)?;
ffi::lua_rawset(self.state, -3);
}
} else {
push_table(self.state, 0, lower_bound as c_int)?;
for (k, v) in iter {
self.push_value(k.to_lua(self)?)?;
self.push_value(v.to_lua(self)?)?;
let protect = !self.unlikely_memory_error();
push_table(self.state, 0, lower_bound as c_int, protect)?;
for (k, v) in iter {
self.push_value(k.to_lua(self)?)?;
self.push_value(v.to_lua(self)?)?;
if protect {
protect_lua!(self.state, 3, 1, fn(state) ffi::lua_rawset(state, -3))?;
} else {
ffi::lua_rawset(self.state, -3);
}
}
@ -1498,20 +1475,16 @@ impl Lua {
let iter = iter.into_iter();
let lower_bound = iter.size_hint().0;
if self.unlikely_memory_error() {
ffi::lua_createtable(self.state, lower_bound as c_int, 0);
for (i, v) in iter.enumerate() {
self.push_value(v.to_lua(self)?)?;
ffi::lua_rawseti(self.state, -2, (i + 1) as Integer);
}
} else {
push_table(self.state, lower_bound as c_int, 0)?;
for (i, v) in iter.enumerate() {
self.push_value(v.to_lua(self)?)?;
let protect = !self.unlikely_memory_error();
push_table(self.state, lower_bound as c_int, 0, protect)?;
for (i, v) in iter.enumerate() {
self.push_value(v.to_lua(self)?)?;
if protect {
protect_lua!(self.state, 2, 1, |state| {
ffi::lua_rawseti(state, -2, (i + 1) as Integer);
})?;
} else {
ffi::lua_rawseti(self.state, -2, (i + 1) as Integer);
}
}
@ -1972,7 +1945,8 @@ impl Lua {
let _sg = StackGuard::new(self.state);
check_stack(self.state, 3)?;
push_string(self.state, name)?;
let protect = !self.unlikely_memory_error();
push_string(self.state, name, protect)?;
ffi::lua_rawget(self.state, ffi::LUA_REGISTRYINDEX);
self.pop_value()
@ -2260,7 +2234,8 @@ impl Lua {
}
Value::Error(err) => {
push_gc_userdata(self.state, WrappedFailure::Error(err))?;
let protect = !self.unlikely_memory_error();
push_gc_userdata(self.state, WrappedFailure::Error(err), protect)?;
}
}
@ -2445,7 +2420,7 @@ impl Lua {
let metatable_nrec = methods.meta_methods.len() + fields.meta_fields.len();
#[cfg(feature = "async")]
let metatable_nrec = metatable_nrec + methods.async_meta_methods.len();
push_table(self.state, 0, metatable_nrec as c_int)?;
push_table(self.state, 0, metatable_nrec as c_int, true)?;
for (k, m) in methods.meta_methods {
self.push_value(Value::Function(self.create_callback(m)?))?;
rawset_field(self.state, -2, k.validate()?.name())?;
@ -2466,7 +2441,7 @@ impl Lua {
let mut field_getters_index = None;
let field_getters_nrec = fields.field_getters.len();
if field_getters_nrec > 0 {
push_table(self.state, 0, field_getters_nrec as c_int)?;
push_table(self.state, 0, field_getters_nrec as c_int, true)?;
for (k, m) in fields.field_getters {
self.push_value(Value::Function(self.create_callback(m)?))?;
rawset_field(self.state, -2, &k)?;
@ -2478,7 +2453,7 @@ impl Lua {
let mut field_setters_index = None;
let field_setters_nrec = fields.field_setters.len();
if field_setters_nrec > 0 {
push_table(self.state, 0, field_setters_nrec as c_int)?;
push_table(self.state, 0, field_setters_nrec as c_int, true)?;
for (k, m) in fields.field_setters {
self.push_value(Value::Function(self.create_callback(m)?))?;
rawset_field(self.state, -2, &k)?;
@ -2492,7 +2467,7 @@ impl Lua {
#[cfg(feature = "async")]
let methods_nrec = methods_nrec + methods.async_methods.len();
if methods_nrec > 0 {
push_table(self.state, 0, methods_nrec as c_int)?;
push_table(self.state, 0, methods_nrec as c_int, true)?;
for (k, m) in methods.methods {
self.push_value(Value::Function(self.create_callback(m)?))?;
rawset_field(self.state, -2, &k)?;
@ -2623,13 +2598,14 @@ impl Lua {
let func = mem::transmute(func);
let extra = Arc::clone(&self.extra);
push_gc_userdata(self.state, CallbackUpvalue { data: func, extra })?;
if self.unlikely_memory_error() {
ffi::lua_pushcclosure(self.state, call_callback, 1);
} else {
let protect = !self.unlikely_memory_error();
push_gc_userdata(self.state, CallbackUpvalue { data: func, extra }, protect)?;
if protect {
protect_lua!(self.state, 1, 1, fn(state) {
ffi::lua_pushcclosure(state, call_callback, 1);
})?;
} else {
ffi::lua_pushcclosure(self.state, call_callback, 1);
}
Ok(Function(self.pop_ref()))
@ -2686,13 +2662,14 @@ impl Lua {
let func = &*(*upvalue).data;
let fut = func(lua, args);
let extra = Arc::clone(&(*upvalue).extra);
push_gc_userdata(state, AsyncPollUpvalue { data: fut, extra })?;
if lua.unlikely_memory_error() {
ffi::lua_pushcclosure(state, poll_future, 1);
} else {
let protect = !lua.unlikely_memory_error();
push_gc_userdata(state, AsyncPollUpvalue { data: fut, extra }, protect)?;
if protect {
protect_lua!(state, 1, 1, fn(state) {
ffi::lua_pushcclosure(state, poll_future, 1);
})?;
} else {
ffi::lua_pushcclosure(state, poll_future, 1);
}
Ok(1)
@ -2752,13 +2729,15 @@ impl Lua {
let func = mem::transmute(func);
let extra = Arc::clone(&self.extra);
push_gc_userdata(self.state, AsyncCallbackUpvalue { data: func, extra })?;
if self.unlikely_memory_error() {
ffi::lua_pushcclosure(self.state, call_callback, 1);
} else {
let protect = !self.unlikely_memory_error();
let upvalue = AsyncCallbackUpvalue { data: func, extra };
push_gc_userdata(self.state, upvalue, protect)?;
if protect {
protect_lua!(self.state, 1, 1, fn(state) {
ffi::lua_pushcclosure(state, call_callback, 1);
})?;
} else {
ffi::lua_pushcclosure(self.state, call_callback, 1);
}
Function(self.pop_ref())
@ -2833,10 +2812,11 @@ impl Lua {
// We push metatable first to ensure having correct metatable with `__gc` method
ffi::lua_pushnil(self.state);
self.push_userdata_metatable::<T>()?;
let protect = !self.unlikely_memory_error();
#[cfg(not(feature = "lua54"))]
push_userdata(self.state, data)?;
push_userdata(self.state, data, protect)?;
#[cfg(feature = "lua54")]
push_userdata_uv(self.state, data, USER_VALUE_MAXSLOT as c_int)?;
push_userdata_uv(self.state, data, USER_VALUE_MAXSLOT as c_int, protect)?;
ffi::lua_replace(self.state, -3);
ffi::lua_setmetatable(self.state, -2);

View File

@ -355,13 +355,14 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
crate::util::push_userdata::<UserDataCell<Rc<RefCell<T>>>>(
lua.state,
UserDataCell::new(data.clone()),
true,
)?;
ffi::lua_touserdata(lua.state, -1)
};
// Prepare metatable, add meta methods first and then meta fields
let meta_methods_nrec = ud_methods.meta_methods.len() + ud_fields.meta_fields.len() + 1;
push_table(lua.state, 0, meta_methods_nrec as c_int)?;
push_table(lua.state, 0, meta_methods_nrec as c_int, true)?;
for (k, m) in ud_methods.meta_methods {
let data = data.clone();
@ -377,7 +378,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
let mut field_getters_index = None;
let field_getters_nrec = ud_fields.field_getters.len();
if field_getters_nrec > 0 {
push_table(lua.state, 0, field_getters_nrec as c_int)?;
push_table(lua.state, 0, field_getters_nrec as c_int, true)?;
for (k, m) in ud_fields.field_getters {
let data = data.clone();
lua.push_value(Value::Function(wrap_method(self, data, ud_ptr, m)?))?;
@ -389,7 +390,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
let mut field_setters_index = None;
let field_setters_nrec = ud_fields.field_setters.len();
if field_setters_nrec > 0 {
push_table(lua.state, 0, field_setters_nrec as c_int)?;
push_table(lua.state, 0, field_setters_nrec as c_int, true)?;
for (k, m) in ud_fields.field_setters {
let data = data.clone();
lua.push_value(Value::Function(wrap_method(self, data, ud_ptr, m)?))?;
@ -402,7 +403,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
let methods_nrec = ud_methods.methods.len();
if methods_nrec > 0 {
// Create table used for methods lookup
push_table(lua.state, 0, methods_nrec as c_int)?;
push_table(lua.state, 0, methods_nrec as c_int, true)?;
for (k, m) in ud_methods.methods {
let data = data.clone();
lua.push_value(Value::Function(wrap_method(self, data, ud_ptr, m)?))?;

View File

@ -327,10 +327,17 @@ impl<'lua> ser::SerializeSeq for SerializeVec<'lua> {
lua.push_ref(&self.table.0);
lua.push_value(value)?;
protect_lua!(lua.state, 2, 0, fn(state) {
let len = ffi::lua_rawlen(state, -2) as Integer;
ffi::lua_rawseti(state, -2, len + 1);
})
if lua.unlikely_memory_error() {
let len = ffi::lua_rawlen(lua.state, -2) as Integer;
ffi::lua_rawseti(lua.state, -2, len + 1);
ffi::lua_pop(lua.state, 1);
Ok(())
} else {
protect_lua!(lua.state, 2, 0, fn(state) {
let len = ffi::lua_rawlen(state, -2) as Integer;
ffi::lua_rawseti(state, -2, len + 1);
})
}
}
}

View File

@ -251,17 +251,33 @@ pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error {
pub unsafe fn push_string<S: AsRef<[u8]> + ?Sized>(
state: *mut ffi::lua_State,
s: &S,
protect: bool,
) -> Result<()> {
let s = s.as_ref();
protect_lua!(state, 0, 1, |state| {
if protect {
protect_lua!(state, 0, 1, |state| {
ffi::lua_pushlstring(state, s.as_ptr() as *const c_char, s.len());
})
} else {
ffi::lua_pushlstring(state, s.as_ptr() as *const c_char, s.len());
})
Ok(())
}
}
// Uses 3 stack spaces, does not call checkstack.
#[inline]
pub unsafe fn push_table(state: *mut ffi::lua_State, narr: c_int, nrec: c_int) -> Result<()> {
protect_lua!(state, 0, 1, |state| ffi::lua_createtable(state, narr, nrec))
pub unsafe fn push_table(
state: *mut ffi::lua_State,
narr: c_int,
nrec: c_int,
protect: bool,
) -> Result<()> {
if protect {
protect_lua!(state, 0, 1, |state| ffi::lua_createtable(state, narr, nrec))
} else {
ffi::lua_createtable(state, narr, nrec);
Ok(())
}
}
// Uses 4 stack spaces, does not call checkstack.
@ -281,10 +297,14 @@ where
// Internally uses 3 stack spaces, does not call checkstack.
#[cfg(not(feature = "luau"))]
#[inline]
pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T) -> Result<()> {
let ud = protect_lua!(state, 0, 1, |state| {
pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T, protect: bool) -> Result<()> {
let ud = if protect {
protect_lua!(state, 0, 1, |state| {
ffi::lua_newuserdata(state, mem::size_of::<T>()) as *mut T
})?
} else {
ffi::lua_newuserdata(state, mem::size_of::<T>()) as *mut T
})?;
};
ptr::write(ud, t);
Ok(())
}
@ -292,7 +312,7 @@ pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T) -> Result<()> {
// Internally uses 3 stack spaces, does not call checkstack.
#[cfg(feature = "luau")]
#[inline]
pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T) -> Result<()> {
pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T, protect: bool) -> Result<()> {
unsafe extern "C" fn destructor<T>(ud: *mut c_void) {
let ud = ud as *mut T;
if *(ud.offset(1) as *mut u8) == 0 {
@ -300,10 +320,14 @@ pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T) -> Result<()> {
}
}
let ud = protect_lua!(state, 0, 1, |state| {
let size = mem::size_of::<T>() + 1;
let size = mem::size_of::<T>() + 1;
let ud = if protect {
protect_lua!(state, 0, 1, |state| {
ffi::lua_newuserdatadtor(state, size, destructor::<T>) as *mut T
})?
} else {
ffi::lua_newuserdatadtor(state, size, destructor::<T>) as *mut T
})?;
};
ptr::write(ud, t);
*(ud.offset(1) as *mut u8) = 0; // Mark as not destructed
@ -313,10 +337,19 @@ pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T) -> Result<()> {
// Internally uses 3 stack spaces, does not call checkstack.
#[cfg(feature = "lua54")]
#[inline]
pub unsafe fn push_userdata_uv<T>(state: *mut ffi::lua_State, t: T, nuvalue: c_int) -> Result<()> {
let ud = protect_lua!(state, 0, 1, |state| {
pub unsafe fn push_userdata_uv<T>(
state: *mut ffi::lua_State,
t: T,
nuvalue: c_int,
protect: bool,
) -> Result<()> {
let ud = if protect {
protect_lua!(state, 0, 1, |state| {
ffi::lua_newuserdatauv(state, mem::size_of::<T>(), nuvalue) as *mut T
})?
} else {
ffi::lua_newuserdatauv(state, mem::size_of::<T>(), nuvalue) as *mut T
})?;
};
ptr::write(ud, t);
Ok(())
}
@ -349,8 +382,12 @@ pub unsafe fn take_userdata<T>(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<T: Any>(state: *mut ffi::lua_State, t: T) -> Result<()> {
push_userdata(state, t)?;
pub unsafe fn push_gc_userdata<T: Any>(
state: *mut ffi::lua_State,
t: T,
protect: bool,
) -> Result<()> {
push_userdata(state, t, protect)?;
get_gc_metatable::<T>(state);
ffi::lua_setmetatable(state, -2);
Ok(())
@ -505,7 +542,7 @@ pub unsafe fn init_userdata_metatable<T>(
// Push `__index` generator function
init_userdata_metatable_index(state)?;
push_string(state, "__index")?;
push_string(state, "__index", true)?;
let index_type = ffi::lua_rawget(state, -3);
match index_type {
ffi::LUA_TNIL | ffi::LUA_TTABLE | ffi::LUA_TFUNCTION => {
@ -530,7 +567,7 @@ pub unsafe fn init_userdata_metatable<T>(
// Push `__newindex` generator function
init_userdata_metatable_newindex(state)?;
push_string(state, "__newindex")?;
push_string(state, "__newindex", true)?;
let newindex_type = ffi::lua_rawget(state, -3);
match newindex_type {
ffi::LUA_TNIL | ffi::LUA_TTABLE | ffi::LUA_TFUNCTION => {
@ -758,7 +795,7 @@ pub unsafe fn init_gc_metatable<T: Any>(
) -> Result<()> {
check_stack(state, 6)?;
push_table(state, 0, 3)?;
push_table(state, 0, 3, true)?;
#[cfg(not(feature = "luau"))]
{
@ -836,7 +873,7 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
}
}?;
push_string(state, &*err_buf)?;
push_string(state, &*err_buf, true)?;
(*err_buf).clear();
Ok(1)
@ -857,7 +894,7 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
callback_error(state, |_| Err(Error::CallbackDestructed))
}
push_table(state, 0, 26)?;
push_table(state, 0, 26, true)?;
ffi::lua_pushcfunction(state, destructed_error);
for &method in &[
"__add",
@ -914,7 +951,7 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
// Create error print buffer
init_gc_metatable::<String>(state, None)?;
push_gc_userdata(state, String::new())?;
push_gc_userdata(state, String::new(), true)?;
protect_lua!(state, 1, 0, fn(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);