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.
This commit is contained in:
Alex Orlenko
2024-06-12 23:18:46 +01:00
parent b46cad1db1
commit a25e81036e
3 changed files with 40 additions and 5 deletions
+1 -1
View File
@@ -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.
+11 -4
View File
@@ -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);
+28
View File
@@ -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(())
}