Skip to content

Commit

Permalink
Do not allow already running coroutines to be reset or resumed.
Browse files Browse the repository at this point in the history
This is a wrong use of the Lua API and is not supported.
See #416 for the reference.
  • Loading branch information
khvzak committed Jun 12, 2024
1 parent b46cad1 commit a25e810
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 5 deletions.
2 changes: 1 addition & 1 deletion src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
15 changes: 11 additions & 4 deletions src/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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)?;
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
Expand Down
28 changes: 28 additions & 0 deletions tests/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
}

Expand Down Expand Up @@ -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(())
}

Expand Down

0 comments on commit a25e810

Please sign in to comment.