Files
Alex Orlenko 63a255bbc9 Replace is_sync specialization trick with MaybeSync trait bound.
The `is_sync::<T>()` runtime check relied on implicit specialization via
`Copy`/`Clone` array behavior, which has changed in Rust 1.86+.
`UserDataRef` always taking an exclusive lock even for `Sync` userdata,
preventing concurrent shared borrows.

With the `send` feature flag enabled, userdata types must now be `Send + Sync`.
This is a breaking change, `T: Send + !Sync` userdata types can be wrapped in a `Mutex`
or used inside a `Scope` where this restriction is lifted.
2026-02-21 15:05:55 +00:00

42 lines
1.2 KiB
Rust

#![cfg(feature = "send")]
use mlua::{AnyUserData, Lua, ObjectLike, Result, UserData, UserDataMethods, UserDataRef};
use static_assertions::assert_impl_all;
#[test]
fn test_userdata_multithread_access_sync() -> Result<()> {
let lua = Lua::new();
// This type is `Send` and `Sync`.
struct MyUserData(String);
assert_impl_all!(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")?;
assert!(ud.call_method::<()>("method2", ()).is_ok());
Ok(this.0.clone())
});
methods.add_method("method2", |_, _, ()| Ok(()));
}
}
lua.globals().set("ud", MyUserData("hello".to_string()))?;
// We acquired the shared reference.
let _ud = lua.globals().get::<UserDataRef<MyUserData>>("ud")?;
std::thread::scope(|s| {
s.spawn(|| {
// Getting another shared reference for `Sync` type is allowed.
let _ = lua.globals().get::<UserDataRef<MyUserData>>("ud").unwrap();
});
});
lua.load("ud:method()").exec().unwrap();
Ok(())
}