#![cfg(feature = "send")]
use std::thread;
use luaur_rt::{
AnyUserData, Buffer, Function, Lua, LuaString, RegistryKey, Result, Table, Thread, UserData,
UserDataMethods, UserDataRef, Value, Vector,
};
fn assert_send<T: Send>() {}
struct Probe<T>(std::marker::PhantomData<T>);
trait NotSyncProbe {
fn is_sync(&self) -> bool {
false
}
}
impl<T> NotSyncProbe for Probe<T> {}
impl<T: Sync> Probe<T> {
#[allow(dead_code)]
fn is_sync(&self) -> bool {
true
}
}
#[test]
fn test_lua_is_send_but_not_sync() {
assert_send::<Lua>();
let probe = Probe::<Lua>(std::marker::PhantomData);
assert!(
!NotSyncProbe::is_sync(&probe),
"Lua must remain !Sync under the `send` feature (move-only contract)"
);
}
#[test]
fn test_lua_and_handles_are_send() {
assert_send::<Lua>();
assert_send::<Table>();
assert_send::<Function>();
assert_send::<LuaString>();
assert_send::<AnyUserData>();
assert_send::<Thread>();
assert_send::<Buffer>();
assert_send::<Vector>();
assert_send::<Value>();
assert_send::<RegistryKey>();
assert_send::<luaur_rt::MultiValue>();
assert_send::<luaur_rt::Error>();
}
#[test]
fn test_move_lua_to_another_thread() -> Result<()> {
let lua = Lua::new();
let captured = String::from("from the spawning thread");
let f = lua.create_function(move |_, ()| Ok(captured.clone()))?;
lua.globals().set("greet", f)?;
lua.globals().set("n", 41i64)?;
let handle = thread::spawn(move || -> Result<(i64, String)> {
let n: i64 = lua.load("return n + 1").eval()?;
let g: String = lua.load("return greet()").eval()?;
Ok((n, g))
});
let (n, g) = handle.join().expect("worker thread panicked")?;
assert_eq!(n, 42);
assert_eq!(g, "from the spawning thread");
Ok(())
}
#[test]
fn test_callback_captures_send_data() -> Result<()> {
let payload: Vec<i64> = thread::spawn(|| vec![1, 2, 3, 4])
.join()
.expect("worker panicked");
let lua = Lua::new();
let sum_fn = lua.create_function(move |_, ()| Ok(payload.iter().sum::<i64>()))?;
lua.globals().set("sum", sum_fn)?;
let total: i64 = lua.load("return sum()").eval()?;
assert_eq!(total, 10);
Ok(())
}
struct MyUserData(String);
fn _assert_my_userdata_send_sync()
where
MyUserData: Send + Sync,
{
}
impl UserData for MyUserData {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_method("method", |lua, this, ()| {
let ud = lua.globals().get::<AnyUserData>("ud")?;
let method2 = lua
.load("return function(u) return u:method2() end")
.eval::<Function>()?;
method2.call::<()>(ud)?;
Ok(this.0.clone())
});
methods.add_method("method2", |_, _, ()| Ok(()));
}
}
#[test]
fn test_userdata_nested_method_call() -> Result<()> {
let lua = Lua::new();
let ud = lua.create_userdata(MyUserData("hello".to_string()))?;
lua.globals().set("ud", ud)?;
{
let any = lua.globals().get::<AnyUserData>("ud")?;
let r: UserDataRef<'_, MyUserData> = any.borrow::<MyUserData>()?;
assert_eq!(r.0, "hello");
}
let out: String = lua.load("return ud:method()").eval()?;
assert_eq!(out, "hello");
Ok(())
}