Skip to content

Commit a25e810

Browse files
committed
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.
1 parent b46cad1 commit a25e810

File tree

3 files changed

+40
-5
lines changed

3 files changed

+40
-5
lines changed

src/error.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ pub enum Error {
101101
/// [`Thread::resume`] was called on an inactive coroutine.
102102
///
103103
/// A coroutine is inactive if its main function has returned or if an error has occurred inside
104-
/// the coroutine.
104+
/// the coroutine. Already running coroutines are also marked as inactive (unresumable).
105105
///
106106
/// [`Thread::status`] can be used to check if the coroutine can be resumed without causing this
107107
/// error.

src/thread.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,10 @@ impl<'lua> Thread<'lua> {
142142
A: IntoLuaMulti<'lua>,
143143
R: FromLuaMulti<'lua>,
144144
{
145+
if self.status() != ThreadStatus::Resumable {
146+
return Err(Error::CoroutineInactive);
147+
}
148+
145149
let lua = self.0.lua;
146150
let state = lua.state();
147151
let thread_state = self.state();
@@ -165,10 +169,6 @@ impl<'lua> Thread<'lua> {
165169
let state = lua.state();
166170
let thread_state = self.state();
167171

168-
if self.status() != ThreadStatus::Resumable {
169-
return Err(Error::CoroutineInactive);
170-
}
171-
172172
let nargs = args.push_into_stack_multi(lua)?;
173173
if nargs > 0 {
174174
check_stack(thread_state, nargs)?;
@@ -196,6 +196,10 @@ impl<'lua> Thread<'lua> {
196196
/// Gets the status of the thread.
197197
pub fn status(&self) -> ThreadStatus {
198198
let thread_state = self.state();
199+
if thread_state == self.0.lua.state() {
200+
// The coroutine is currently running
201+
return ThreadStatus::Unresumable;
202+
}
199203
unsafe {
200204
let status = ffi::lua_status(thread_state);
201205
if status != ffi::LUA_OK && status != ffi::LUA_YIELD {
@@ -243,6 +247,9 @@ impl<'lua> Thread<'lua> {
243247
pub fn reset(&self, func: crate::function::Function<'lua>) -> Result<()> {
244248
let lua = self.0.lua;
245249
let thread_state = self.state();
250+
if thread_state == lua.state() {
251+
return Err(Error::runtime("cannot reset a running thread"));
252+
}
246253
unsafe {
247254
#[cfg(all(feature = "lua54", not(feature = "vendored")))]
248255
let status = ffi::lua_resetthread(thread_state);

tests/thread.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,19 @@ fn test_thread() -> Result<()> {
9090
_ => panic!("resuming dead coroutine did not return error"),
9191
}
9292

93+
// Already running thread must be unresumable
94+
let thread = lua.create_thread(lua.create_function(|lua, ()| {
95+
assert_eq!(lua.current_thread().status(), ThreadStatus::Unresumable);
96+
let result = lua.current_thread().resume::<_, ()>(());
97+
assert!(
98+
matches!(result, Err(Error::CoroutineInactive)),
99+
"unexpected result: {result:?}",
100+
);
101+
Ok(())
102+
})?)?;
103+
let result = thread.resume::<_, ()>(());
104+
assert!(result.is_ok(), "unexpected result: {result:?}");
105+
93106
Ok(())
94107
}
95108

@@ -146,6 +159,21 @@ fn test_thread_reset() -> Result<()> {
146159
assert_eq!(thread.status(), ThreadStatus::Resumable);
147160
}
148161

162+
// Try reset running thread
163+
let thread = lua.create_thread(lua.create_function(|lua, ()| {
164+
let this = lua.current_thread();
165+
this.reset(lua.create_function(|_, ()| Ok(()))?)?;
166+
Ok(())
167+
})?)?;
168+
let result = thread.resume::<_, ()>(());
169+
assert!(
170+
matches!(result, Err(Error::CallbackError{ ref cause, ..})
171+
if matches!(cause.as_ref(), Error::RuntimeError(ref err)
172+
if err == "cannot reset a running thread")
173+
),
174+
"unexpected result: {result:?}",
175+
);
176+
149177
Ok(())
150178
}
151179

0 commit comments

Comments
 (0)