From 318f0e3475f7af5427e262c70d5e1ed522e2ce13 Mon Sep 17 00:00:00 2001 From: William Venner Date: Thu, 27 Jan 2022 18:46:58 +0000 Subject: [PATCH] Add more coroutine bindings and utility functions --- gmod/src/lua/import.rs | 4 ++ gmod/src/lua/lua_state.rs | 96 ++++++++++++++++++++++++++++++--------- 2 files changed, 78 insertions(+), 22 deletions(-) diff --git a/gmod/src/lua/import.rs b/gmod/src/lua/import.rs index d7947fb..9875a8c 100644 --- a/gmod/src/lua/import.rs +++ b/gmod/src/lua/import.rs @@ -171,6 +171,8 @@ pub struct LuaShared { pub lua_yield: Symbol<'static, unsafe extern "C-unwind" fn(state: LuaState, nresults: i32) -> i32>, pub lua_pushthread: Symbol<'static, unsafe extern "C-unwind" fn(state: LuaState) -> i32>, pub lua_tothread: Symbol<'static, unsafe extern "C-unwind" fn(state: LuaState, index: i32) -> LuaState>, + pub lua_status: Symbol<'static, unsafe extern "C-unwind" fn(state: LuaState) -> i32>, + pub lua_xmove: Symbol<'static, unsafe extern "C-unwind" fn(thread1: LuaState, thread2: LuaState, n: i32)>, } unsafe impl Sync for LuaShared {} impl LuaShared { @@ -243,6 +245,8 @@ impl LuaShared { lua_yield: find_symbol!("lua_yield"), lua_pushthread: find_symbol!("lua_pushthread"), lua_tothread: find_symbol!("lua_tothread"), + lua_status: find_symbol!("lua_status"), + lua_xmove: find_symbol!("lua_xmove"), } } } diff --git a/gmod/src/lua/lua_state.rs b/gmod/src/lua/lua_state.rs index fa82160..6befe58 100644 --- a/gmod/src/lua/lua_state.rs +++ b/gmod/src/lua/lua_state.rs @@ -4,6 +4,26 @@ use crate::lua::*; use crate::userdata::TaggedUserData; +unsafe fn handle_pcall_ignore(lua: State) { + crate::lua_stack_guard!(lua => { + lua.get_global(crate::lua_string!("ErrorNoHaltWithStack")); + if lua.is_nil(-1) { + eprintln!("[ERROR] {:?}", lua.get_string(-2)); + lua.pop(); + } else { + #[cfg(debug_assertions)] { + lua.push_string(&format!("[pcall_ignore] {}", lua.get_string(-2).expect("Expected a string here"))); + } + #[cfg(not(debug_assertions))] { + lua.push_value(-2); + } + + lua.call(1, 0); + } + }); + lua.pop(); +} + #[repr(transparent)] #[derive(Clone, Copy, Debug)] pub struct LuaState(pub *mut std::ffi::c_void); @@ -193,28 +213,17 @@ impl LuaState { /// /// Returns whether the execution was successful. pub unsafe fn pcall_ignore(&self, nargs: i32, nresults: i32) -> bool { - let res = self.pcall(nargs, nresults, 0); - if res == LUA_ERRRUN { - crate::lua_stack_guard!(self => { - self.get_global(crate::lua_string!("ErrorNoHaltWithStack")); - if self.is_nil(-1) { - eprintln!("[ERROR] {:?}", self.get_string(-2)); - self.pop(); - } else { - #[cfg(debug_assertions)] { - self.push_string(&format!("[pcall_ignore] {}", self.get_string(-2).expect("Expected a string here"))); - } - #[cfg(not(debug_assertions))] { - self.push_value(-2); - } - - self.call(1, 0); - } - }); - self.pop(); - false - } else { - true + match self.pcall(nargs, nresults, 0) { + LUA_OK => true, + LUA_ERRRUN => { + handle_pcall_ignore(*self); + false + } + err @ _ => { + #[cfg(debug_assertions)] + eprintln!("[gmod-rs] pcall_ignore unknown error: {}", err); + false + } } } @@ -483,15 +492,58 @@ impl LuaState { } #[inline(always)] + #[must_use] pub unsafe fn coroutine_yield(&self, nresults: i32) -> i32 { (LUA_SHARED.lua_yield)(*self, nresults) } #[inline(always)] + #[must_use] pub unsafe fn coroutine_resume(&self, narg: i32) -> i32 { (LUA_SHARED.lua_resume)(*self, narg) } + #[inline(always)] + /// Exchange values between different threads of the same global state. + /// + /// This function pops `n` values from the stack `self`, and pushes them onto the stack `target_thread`. + pub unsafe fn coroutine_exchange(&self, target_thread: State, n: i32) { + (LUA_SHARED.lua_xmove)(*self, target_thread, n) + } + + #[inline(always)] + /// See `call` + pub unsafe fn coroutine_resume_call(&self, narg: i32) { + match (LUA_SHARED.lua_resume)(*self, narg) { + LUA_OK => {}, + LUA_ERRRUN => self.error(self.get_string(-2).unwrap_or_else(|| Cow::Borrowed("Unknown error")).as_ref()), + LUA_ERRMEM => self.error("Out of memory"), + _ => self.error("Unknown internal Lua error") + } + } + + #[inline(always)] + /// See `pcall_ignore` + pub unsafe fn coroutine_resume_pcall_ignore(&self, narg: i32) -> Result { + match (LUA_SHARED.lua_resume)(*self, narg) { + status @ (LUA_OK | LUA_YIELD) => Ok(status), + LUA_ERRRUN => { + handle_pcall_ignore(*self); + Err(()) + }, + err @ _ => { + #[cfg(debug_assertions)] + eprintln!("[gmod-rs] coroutine_resume_pcall_ignore unknown error: {}", err); + Err(()) + } + } + } + + #[inline(always)] + pub unsafe fn coroutine_status(&self) -> i32 { + (LUA_SHARED.lua_status)(*self) + } + /// Creates a new table in the registry with the given `name` as the key if it doesn't already exist, and pushes it onto the stack. /// /// Returns if the metatable was already present in the registry.