Skip to content

Commit 3f0c69b

Browse files
committed
Fix logic to terminate futures on drop.
The underlying Lua thread must stay in yielded state rather than finished.
1 parent 9da98d4 commit 3f0c69b

File tree

3 files changed

+36
-1
lines changed

3 files changed

+36
-1
lines changed

src/state/raw.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1262,7 +1262,7 @@ impl RawLua {
12621262
if nargs == 1 && ffi::lua_tolightuserdata(state, -1) == Lua::poll_terminate().0 {
12631263
// Destroy the future and terminate the Lua thread
12641264
(*upvalue).data.take();
1265-
ffi::lua_pushinteger(state, 0);
1265+
ffi::lua_pushinteger(state, -1);
12661266
return Ok(1);
12671267
}
12681268

@@ -1347,6 +1347,10 @@ impl RawLua {
13471347
return res
13481348
elseif nres == 2 then
13491349
return res, res2
1350+
elseif nres < 0 then
1351+
-- Negative `nres` means that the future is terminated
1352+
-- It must stay yielded and never be resumed again
1353+
yield()
13501354
else
13511355
return unpack(res, nres)
13521356
end

src/thread.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,7 @@ impl<R> Drop for AsyncThread<R> {
529529
// The thread is dropped while yielded, resume it with the "terminate" signal
530530
ffi::lua_pushlightuserdata(self.thread.1, crate::Lua::poll_terminate().0);
531531
if let Ok((new_status, _)) = self.thread.resume_inner(&lua, 1) {
532+
// `new_status` should always be `ThreadStatusInner::Yielded(0)`
532533
status = new_status;
533534
}
534535
}

tests/async.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,36 @@ async fn test_async_task() -> Result<()> {
610610
Ok(())
611611
}
612612

613+
#[tokio::test]
614+
async fn test_async_task_abort() -> Result<()> {
615+
let lua = Lua::new();
616+
617+
let sleep = lua.create_async_function(move |_lua, n: u64| async move {
618+
sleep_ms(n).await;
619+
Ok(())
620+
})?;
621+
lua.globals().set("sleep", sleep)?;
622+
623+
let local = tokio::task::LocalSet::new();
624+
local
625+
.run_until(async {
626+
let lua2 = lua.clone();
627+
let jh = tokio::task::spawn_local(async move {
628+
lua2.load("sleep(200) result = 'done'")
629+
.exec_async()
630+
.await
631+
.unwrap();
632+
});
633+
sleep_ms(100).await; // Wait for the task to start
634+
jh.abort();
635+
})
636+
.await;
637+
local.await;
638+
assert_eq!(lua.globals().get::<Value>("result")?, Value::Nil);
639+
640+
Ok(())
641+
}
642+
613643
#[tokio::test]
614644
#[cfg(not(feature = "luau"))]
615645
async fn test_async_hook() -> Result<()> {

0 commit comments

Comments
 (0)