Skip to content

Commit 347856b

Browse files
committed
Do not try to yield at non-yielable points in Luau interrupt
In particular we cannot yeild across metamethod/C-call boundaries. This behaviour matches with Lua 5.3+ yielding from hooks only at safe points. Closes #632
1 parent 774a63b commit 347856b

File tree

2 files changed

+16
-4
lines changed

2 files changed

+16
-4
lines changed

src/state.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -631,13 +631,13 @@ impl Lua {
631631
///
632632
/// Any Luau code is guaranteed to call this handler "eventually"
633633
/// (in practice this can happen at any function call or at any loop iteration).
634+
/// This is similar to `Lua::set_hook` but in more simplified form.
634635
///
635636
/// The provided interrupt function can error, and this error will be propagated through
636637
/// the Luau code that was executing at the time the interrupt was triggered.
637638
/// Also this can be used to implement continuous execution limits by instructing Luau VM to
638-
/// yield by returning [`VmState::Yield`].
639-
///
640-
/// This is similar to `Lua::set_hook` but in more simplified form.
639+
/// yield by returning [`VmState::Yield`]. The yield will happen only at yieldable points
640+
/// of execution (not across metamethod/C-call boundaries).
641641
///
642642
/// # Example
643643
///
@@ -695,7 +695,10 @@ impl Lua {
695695
match result {
696696
VmState::Continue => {}
697697
VmState::Yield => {
698-
ffi::lua_yield(state, 0);
698+
// We can yield only at yieldable points, otherwise ignore and continue
699+
if ffi::lua_isyieldable(state) != 0 {
700+
ffi::lua_yield(state, 0);
701+
}
699702
}
700703
}
701704
}

tests/luau.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,15 @@ fn test_interrupts() -> Result<()> {
330330
assert_eq!(yield_count.load(Ordering::Relaxed), 7);
331331
assert_eq!(co.status(), ThreadStatus::Finished);
332332

333+
// Test no yielding at non-yieldable points
334+
yield_count.store(0, Ordering::Relaxed);
335+
let co = lua.create_thread(lua.create_function(|lua, arg: Value| {
336+
(lua.load("return (function(x) return x end)(...)")).call::<Value>(arg)
337+
})?)?;
338+
let res = co.resume::<String>("abc")?;
339+
assert_eq!(res, "abc".to_string());
340+
assert_eq!(yield_count.load(Ordering::Relaxed), 3);
341+
333342
//
334343
// Test errors in interrupts
335344
//

0 commit comments

Comments
 (0)