From a25e81036eb07049db2a20b48bc243410ea1b757 Mon Sep 17 00:00:00 2001 From: Alex Orlenko Date: Wed, 12 Jun 2024 23:18:46 +0100 Subject: [PATCH] Do not allow already running coroutines to be reset or resumed. This is a wrong use of the Lua API and is not supported. See #416 for the reference. --- src/error.rs | 2 +- src/thread.rs | 15 +++++++++++---- tests/thread.rs | 28 ++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/src/error.rs b/src/error.rs index 2a86eac..8c563c2 100644 --- a/src/error.rs +++ b/src/error.rs @@ -101,7 +101,7 @@ pub enum Error { /// [`Thread::resume`] was called on an inactive coroutine. /// /// A coroutine is inactive if its main function has returned or if an error has occurred inside - /// the coroutine. + /// the coroutine. Already running coroutines are also marked as inactive (unresumable). /// /// [`Thread::status`] can be used to check if the coroutine can be resumed without causing this /// error. diff --git a/src/thread.rs b/src/thread.rs index 5bd634e..4c003a7 100644 --- a/src/thread.rs +++ b/src/thread.rs @@ -142,6 +142,10 @@ impl<'lua> Thread<'lua> { A: IntoLuaMulti<'lua>, R: FromLuaMulti<'lua>, { + if self.status() != ThreadStatus::Resumable { + return Err(Error::CoroutineInactive); + } + let lua = self.0.lua; let state = lua.state(); let thread_state = self.state(); @@ -165,10 +169,6 @@ impl<'lua> Thread<'lua> { let state = lua.state(); let thread_state = self.state(); - if self.status() != ThreadStatus::Resumable { - return Err(Error::CoroutineInactive); - } - let nargs = args.push_into_stack_multi(lua)?; if nargs > 0 { check_stack(thread_state, nargs)?; @@ -196,6 +196,10 @@ impl<'lua> Thread<'lua> { /// Gets the status of the thread. pub fn status(&self) -> ThreadStatus { let thread_state = self.state(); + if thread_state == self.0.lua.state() { + // The coroutine is currently running + return ThreadStatus::Unresumable; + } unsafe { let status = ffi::lua_status(thread_state); if status != ffi::LUA_OK && status != ffi::LUA_YIELD { @@ -243,6 +247,9 @@ impl<'lua> Thread<'lua> { pub fn reset(&self, func: crate::function::Function<'lua>) -> Result<()> { let lua = self.0.lua; let thread_state = self.state(); + if thread_state == lua.state() { + return Err(Error::runtime("cannot reset a running thread")); + } unsafe { #[cfg(all(feature = "lua54", not(feature = "vendored")))] let status = ffi::lua_resetthread(thread_state); diff --git a/tests/thread.rs b/tests/thread.rs index 83beb4f..2bc1988 100644 --- a/tests/thread.rs +++ b/tests/thread.rs @@ -90,6 +90,19 @@ fn test_thread() -> Result<()> { _ => panic!("resuming dead coroutine did not return error"), } + // Already running thread must be unresumable + let thread = lua.create_thread(lua.create_function(|lua, ()| { + assert_eq!(lua.current_thread().status(), ThreadStatus::Unresumable); + let result = lua.current_thread().resume::<_, ()>(()); + assert!( + matches!(result, Err(Error::CoroutineInactive)), + "unexpected result: {result:?}", + ); + Ok(()) + })?)?; + let result = thread.resume::<_, ()>(()); + assert!(result.is_ok(), "unexpected result: {result:?}"); + Ok(()) } @@ -146,6 +159,21 @@ fn test_thread_reset() -> Result<()> { assert_eq!(thread.status(), ThreadStatus::Resumable); } + // Try reset running thread + let thread = lua.create_thread(lua.create_function(|lua, ()| { + let this = lua.current_thread(); + this.reset(lua.create_function(|_, ()| Ok(()))?)?; + Ok(()) + })?)?; + let result = thread.resume::<_, ()>(()); + assert!( + matches!(result, Err(Error::CallbackError{ ref cause, ..}) + if matches!(cause.as_ref(), Error::RuntimeError(ref err) + if err == "cannot reset a running thread") + ), + "unexpected result: {result:?}", + ); + Ok(()) }