use std::sync::atomic::{AtomicBool, Ordering};
use omnilua::Lua;
const MARKER: &str = "t2b2-panic-hook-chaining-marker-9f3c";
static PREVIOUS_HOOK_RAN: AtomicBool = AtomicBool::new(false);
#[test]
fn non_threadclose_panic_in_resumed_coroutine_reaches_previous_hook() {
let prev = std::panic::take_hook();
std::panic::set_hook(Box::new(move |info| {
let is_marker = info
.payload()
.downcast_ref::<&str>()
.is_some_and(|s| *s == MARKER)
|| info
.payload()
.downcast_ref::<String>()
.is_some_and(|s| s == MARKER);
if is_marker {
PREVIOUS_HOOK_RAN.store(true, Ordering::SeqCst);
} else {
prev(info);
}
}));
let lua = Lua::new();
let warm_up = lua
.create_function(|_lua, ()| Ok(7_i64))
.expect("warm-up callback should create");
lua.globals()
.set("warm_up", warm_up)
.expect("warm-up callback should register");
let warmed: i64 = lua
.load(
r#"
local co = coroutine.create(function() return warm_up() end)
local ok, v = coroutine.resume(co)
assert(ok, v)
return v
"#,
)
.eval()
.expect("warm-up resume should install the chaining hook and return");
assert_eq!(warmed, 7, "warm-up resume must run normally");
assert!(
!PREVIOUS_HOOK_RAN.load(Ordering::SeqCst),
"the previous hook must not have fired during the non-panicking warm-up"
);
let boom = lua
.create_function(|_lua, ()| -> omnilua::Result<i64> {
std::panic::panic_any(MARKER)
})
.expect("panicking callback should create");
lua.globals()
.set("boom", boom)
.expect("panicking callback should register");
let result = lua
.load(
r#"
local co = coroutine.create(function() return boom() end)
local ok, err = coroutine.resume(co)
assert(ok, err)
"#,
)
.exec();
assert!(
result.is_err(),
"the panic must still propagate to the embedder as a Lua error, \
not be silently swallowed"
);
assert!(
PREVIOUS_HOOK_RAN.load(Ordering::SeqCst),
"the chaining hook must delegate a non-LuaThreadClose payload to the \
previously installed hook"
);
}