mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
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:
+1
-1
@@ -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
@@ -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);
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user