From c58f67b140f09315855a2a67de8e1b05c96e5fe7 Mon Sep 17 00:00:00 2001 From: Alex Orlenko Date: Sat, 10 Aug 2024 17:55:04 +0100 Subject: [PATCH] Add `MaybeSend` requirement to Lua futures --- examples/async_tcp_server.rs | 6 +++--- src/function.rs | 8 +++----- src/lib.rs | 3 ++- src/state.rs | 16 +++++++++++----- src/state/raw.rs | 2 +- src/thread.rs | 4 ++++ src/types.rs | 13 ++++++++++--- src/userdata.rs | 12 ++++++------ src/userdata/registry.rs | 37 +++++++++++++++++------------------- tests/async.rs | 5 +++-- 10 files changed, 60 insertions(+), 46 deletions(-) diff --git a/examples/async_tcp_server.rs b/examples/async_tcp_server.rs index 676482d..7ceb312 100644 --- a/examples/async_tcp_server.rs +++ b/examples/async_tcp_server.rs @@ -4,7 +4,7 @@ use std::net::SocketAddr; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::net::{TcpListener, TcpStream}; -use mlua::{chunk, Function, Lua, String as LuaString, UserData, UserDataMethods}; +use mlua::{chunk, BString, Function, Lua, UserData, UserDataMethods}; struct LuaTcpStream(TcpStream); @@ -19,8 +19,8 @@ impl UserData for LuaTcpStream { lua.create_string(&buf) }); - methods.add_async_method_mut("write", |_, this, data: LuaString| async move { - let n = this.0.write(&data.as_bytes()).await?; + methods.add_async_method_mut("write", |_, this, data: BString| async move { + let n = this.0.write(&data).await?; Ok(n) }); diff --git a/src/function.rs b/src/function.rs index 8c4aaa7..2d3e37f 100644 --- a/src/function.rs +++ b/src/function.rs @@ -559,17 +559,15 @@ impl Function { A: FromLuaMulti, R: IntoLuaMulti, F: Fn(&Lua, A) -> FR + MaybeSend + 'static, - FR: Future> + 'static, + FR: Future> + MaybeSend + 'static, { - WrappedAsyncFunction(Box::new(move |rawlua, args| unsafe { - let lua = rawlua.lua(); + WrappedAsyncFunction(Box::new(move |lua, args| unsafe { let args = match A::from_lua_args(args, 1, None, lua) { Ok(args) => args, Err(e) => return Box::pin(future::ready(Err(e))), }; let fut = func(lua, args); - let weak = rawlua.weak().clone(); - Box::pin(async move { fut.await?.push_into_stack_multi(&weak.lock()) }) + Box::pin(async move { fut.await?.push_into_stack_multi(lua.raw_lua()) }) })) } } diff --git a/src/lib.rs b/src/lib.rs index 61a06f2..5f5985b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -100,6 +100,7 @@ mod value; pub mod prelude; +pub use bstr::BString; pub use ffi::{self, lua_CFunction, lua_State}; pub use crate::chunk::{AsChunk, Chunk, ChunkMode}; @@ -113,7 +114,7 @@ pub use crate::stdlib::StdLib; pub use crate::string::{BorrowedBytes, BorrowedStr, String}; pub use crate::table::{Table, TableExt, TablePairs, TableSequence}; pub use crate::thread::{Thread, ThreadStatus}; -pub use crate::types::{AppDataRef, AppDataRefMut, Integer, LightUserData, Number, RegistryKey}; +pub use crate::types::{AppDataRef, AppDataRefMut, Integer, LightUserData, MaybeSend, Number, RegistryKey}; pub use crate::userdata::{ AnyUserData, AnyUserDataExt, MetaMethod, UserData, UserDataFields, UserDataMetatable, UserDataMethods, UserDataRef, UserDataRefMut, UserDataRegistry, diff --git a/src/state.rs b/src/state.rs index 9734a60..ae479d2 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,6 +1,5 @@ use std::any::TypeId; use std::cell::RefCell; -// use std::collections::VecDeque; use std::marker::PhantomData; use std::ops::Deref; use std::os::raw::{c_int, c_void}; @@ -1163,17 +1162,16 @@ impl Lua { 'lua: 'a, F: Fn(&'a Lua, A) -> FR + MaybeSend + 'static, A: FromLuaMulti, - FR: Future> + 'a, + FR: Future> + MaybeSend + 'a, R: IntoLuaMulti, { - (self.lock()).create_async_callback(Box::new(move |rawlua, args| unsafe { - let lua = rawlua.lua(); + (self.lock()).create_async_callback(Box::new(move |lua, args| unsafe { let args = match A::from_lua_args(args, 1, None, lua) { Ok(args) => args, Err(e) => return Box::pin(future::ready(Err(e))), }; let fut = func(lua, args); - Box::pin(async move { fut.await?.push_into_stack_multi(rawlua) }) + Box::pin(async move { fut.await?.push_into_stack_multi(lua.raw_lua()) }) })) } @@ -1840,6 +1838,14 @@ impl Lua { pub(crate) fn weak(&self) -> WeakLua { WeakLua(XRc::downgrade(&self.0)) } + + /// Returns a handle to the unprotected Lua state without any synchronization. + /// + /// This is useful where we know that the lock is already held by the caller. + #[inline(always)] + pub(crate) unsafe fn raw_lua(&self) -> &RawLua { + &*self.0.data_ptr() + } } impl WeakLua { diff --git a/src/state/raw.rs b/src/state/raw.rs index 959ccd6..45c35ad 100644 --- a/src/state/raw.rs +++ b/src/state/raw.rs @@ -1092,7 +1092,7 @@ impl RawLua { let args = MultiValue::from_stack_multi(nargs, rawlua)?; let func = &*(*upvalue).data; - let fut = func(rawlua, args); + let fut = func(rawlua.lua(), args); let extra = XRc::clone(&(*upvalue).extra); let protect = !rawlua.unlikely_memory_error(); push_internal_userdata(state, AsyncPollUpvalue { data: fut, extra }, protect)?; diff --git a/src/thread.rs b/src/thread.rs index 8c9b9d3..b11d0a8 100644 --- a/src/thread.rs +++ b/src/thread.rs @@ -528,4 +528,8 @@ mod assertions { static_assertions::assert_not_impl_any!(Thread: Send); #[cfg(feature = "send")] static_assertions::assert_impl_all!(Thread: Send, Sync); + #[cfg(all(feature = "async", not(feature = "send")))] + static_assertions::assert_not_impl_any!(AsyncThread<()>: Send); + #[cfg(all(feature = "async", feature = "send"))] + static_assertions::assert_impl_all!(AsyncThread<()>: Send, Sync); } diff --git a/src/types.rs b/src/types.rs index 9060bfa..8462a09 100644 --- a/src/types.rs +++ b/src/types.rs @@ -13,7 +13,13 @@ use crate::hook::Debug; use crate::state::{ExtraData, Lua, RawLua, WeakLua}; #[cfg(feature = "async")] -use {crate::value::MultiValue, futures_util::future::LocalBoxFuture}; +use crate::value::MultiValue; + +#[cfg(all(feature = "async", feature = "send"))] +pub(crate) type BoxFuture<'a, T> = futures_util::future::BoxFuture<'a, T>; + +#[cfg(all(feature = "async", not(feature = "send")))] +pub(crate) type BoxFuture<'a, T> = futures_util::future::LocalBoxFuture<'a, T>; #[cfg(all(feature = "luau", feature = "serialize"))] use serde::ser::{Serialize, SerializeTupleStruct, Serializer}; @@ -57,13 +63,13 @@ pub(crate) type CallbackUpvalue = Upvalue>; #[cfg(feature = "async")] pub(crate) type AsyncCallback<'a> = - Box LocalBoxFuture<'a, Result> + 'static>; + Box BoxFuture<'a, Result> + 'static>; #[cfg(feature = "async")] pub(crate) type AsyncCallbackUpvalue = Upvalue>; #[cfg(feature = "async")] -pub(crate) type AsyncPollUpvalue = Upvalue>>; +pub(crate) type AsyncPollUpvalue = Upvalue>>; /// Type to set next Luau VM action after executing interrupt function. #[cfg(any(feature = "luau", doc))] @@ -91,6 +97,7 @@ pub(crate) type WarnCallback = Box Result<()> + Send #[cfg(all(not(feature = "send"), feature = "lua54"))] pub(crate) type WarnCallback = Box Result<()>>; +/// A trait that adds `Send` requirement if `send` feature is enabled. #[cfg(feature = "send")] pub trait MaybeSend: Send {} #[cfg(feature = "send")] diff --git a/src/userdata.rs b/src/userdata.rs index d2f48ac..2ebe46b 100644 --- a/src/userdata.rs +++ b/src/userdata.rs @@ -287,7 +287,7 @@ pub trait UserDataMethods<'a, T> { T: 'static, M: Fn(&'a Lua, &'a T, A) -> MR + MaybeSend + 'static, A: FromLuaMulti, - MR: Future> + 'a, + MR: Future> + MaybeSend + 'a, R: IntoLuaMulti; /// Add an async method which accepts a `&mut T` as the first parameter and returns Future. @@ -304,7 +304,7 @@ pub trait UserDataMethods<'a, T> { T: 'static, M: Fn(&'a Lua, &'a mut T, A) -> MR + MaybeSend + 'static, A: FromLuaMulti, - MR: Future> + 'a, + MR: Future> + MaybeSend + 'a, R: IntoLuaMulti; /// Add a regular method as a function which accepts generic arguments, the first argument will @@ -348,7 +348,7 @@ pub trait UserDataMethods<'a, T> { where F: Fn(&'a Lua, A) -> FR + MaybeSend + 'static, A: FromLuaMulti, - FR: Future> + 'a, + FR: Future> + MaybeSend + 'a, R: IntoLuaMulti; /// Add a metamethod which accepts a `&T` as the first parameter. @@ -393,7 +393,7 @@ pub trait UserDataMethods<'a, T> { T: 'static, M: Fn(&'a Lua, &'a T, A) -> MR + MaybeSend + 'static, A: FromLuaMulti, - MR: Future> + 'a, + MR: Future> + MaybeSend + 'a, R: IntoLuaMulti; /// Add an async metamethod which accepts a `&mut T` as the first parameter and returns Future. @@ -410,7 +410,7 @@ pub trait UserDataMethods<'a, T> { T: 'static, M: Fn(&'a Lua, &'a mut T, A) -> MR + MaybeSend + 'static, A: FromLuaMulti, - MR: Future> + 'a, + MR: Future> + MaybeSend + 'a, R: IntoLuaMulti; /// Add a metamethod which accepts generic arguments. @@ -448,7 +448,7 @@ pub trait UserDataMethods<'a, T> { where F: Fn(&'a Lua, A) -> FR + MaybeSend + 'static, A: FromLuaMulti, - FR: Future> + 'a, + FR: Future> + MaybeSend + 'a, R: IntoLuaMulti; } diff --git a/src/userdata/registry.rs b/src/userdata/registry.rs index 45d301a..de31f30 100644 --- a/src/userdata/registry.rs +++ b/src/userdata/registry.rs @@ -132,7 +132,7 @@ impl<'a, T: 'static> UserDataRegistry<'a, T> { where M: Fn(&'a Lua, &'a T, A) -> MR + MaybeSend + 'static, A: FromLuaMulti, - MR: Future> + 'a, + MR: Future> + MaybeSend + 'a, R: IntoLuaMulti, { let name = get_function_name::(name); @@ -145,15 +145,14 @@ impl<'a, T: 'static> UserDataRegistry<'a, T> { }; } - Box::new(move |rawlua, mut args| unsafe { + Box::new(move |lua, mut args| unsafe { let this = args .pop_front() .ok_or_else(|| Error::from_lua_conversion("missing argument", "userdata", None)); - let lua = rawlua.lua(); let this = try_self_arg!(AnyUserData::from_lua(try_self_arg!(this), lua)); let args = A::from_lua_args(args, 2, Some(&name), lua); - let (ref_thread, index) = (rawlua.ref_thread(), this.0.index); + let (ref_thread, index) = (lua.raw_lua().ref_thread(), this.0.index); match try_self_arg!(this.type_id()) { Some(id) if id == TypeId::of::() => { let ud = try_self_arg!(borrow_userdata_ref::(ref_thread, index)); @@ -162,7 +161,7 @@ impl<'a, T: 'static> UserDataRegistry<'a, T> { Err(e) => return Box::pin(future::ready(Err(e))), }; let fut = method(lua, ud.get_ref(), args); - Box::pin(async move { fut.await?.push_into_stack_multi(rawlua) }) + Box::pin(async move { fut.await?.push_into_stack_multi(lua.raw_lua()) }) } _ => { let err = Error::bad_self_argument(&name, Error::UserDataTypeMismatch); @@ -177,7 +176,7 @@ impl<'a, T: 'static> UserDataRegistry<'a, T> { where M: Fn(&'a Lua, &'a mut T, A) -> MR + MaybeSend + 'static, A: FromLuaMulti, - MR: Future> + 'a, + MR: Future> + MaybeSend + 'a, R: IntoLuaMulti, { let name = get_function_name::(name); @@ -190,15 +189,14 @@ impl<'a, T: 'static> UserDataRegistry<'a, T> { }; } - Box::new(move |rawlua, mut args| unsafe { + Box::new(move |lua, mut args| unsafe { let this = args .pop_front() .ok_or_else(|| Error::from_lua_conversion("missing argument", "userdata", None)); - let lua = rawlua.lua(); let this = try_self_arg!(AnyUserData::from_lua(try_self_arg!(this), lua)); let args = A::from_lua_args(args, 2, Some(&name), lua); - let (ref_thread, index) = (rawlua.ref_thread(), this.0.index); + let (ref_thread, index) = (lua.raw_lua().ref_thread(), this.0.index); match try_self_arg!(this.type_id()) { Some(id) if id == TypeId::of::() => { let mut ud = try_self_arg!(borrow_userdata_mut::(ref_thread, index)); @@ -207,7 +205,7 @@ impl<'a, T: 'static> UserDataRegistry<'a, T> { Err(e) => return Box::pin(future::ready(Err(e))), }; let fut = method(lua, ud.get_mut(), args); - Box::pin(async move { fut.await?.push_into_stack_multi(rawlua) }) + Box::pin(async move { fut.await?.push_into_stack_multi(lua.raw_lua()) }) } _ => { let err = Error::bad_self_argument(&name, Error::UserDataTypeMismatch); @@ -252,18 +250,17 @@ impl<'a, T: 'static> UserDataRegistry<'a, T> { where F: Fn(&'a Lua, A) -> FR + MaybeSend + 'static, A: FromLuaMulti, - FR: Future> + 'a, + FR: Future> + MaybeSend + 'a, R: IntoLuaMulti, { let name = get_function_name::(name); - Box::new(move |rawlua, args| unsafe { - let lua = rawlua.lua(); + Box::new(move |lua, args| unsafe { let args = match A::from_lua_args(args, 1, Some(&name), lua) { Ok(args) => args, Err(e) => return Box::pin(future::ready(Err(e))), }; let fut = function(lua, args); - Box::pin(async move { fut.await?.push_into_stack_multi(rawlua) }) + Box::pin(async move { fut.await?.push_into_stack_multi(lua.raw_lua()) }) }) } @@ -397,7 +394,7 @@ impl<'a, T: 'static> UserDataMethods<'a, T> for UserDataRegistry<'a, T> { where M: Fn(&'a Lua, &'a T, A) -> MR + MaybeSend + 'static, A: FromLuaMulti, - MR: Future> + 'a, + MR: Future> + MaybeSend + 'a, R: IntoLuaMulti, { let name = name.to_string(); @@ -410,7 +407,7 @@ impl<'a, T: 'static> UserDataMethods<'a, T> for UserDataRegistry<'a, T> { where M: Fn(&'a Lua, &'a mut T, A) -> MR + MaybeSend + 'static, A: FromLuaMulti, - MR: Future> + 'a, + MR: Future> + MaybeSend + 'a, R: IntoLuaMulti, { let name = name.to_string(); @@ -445,7 +442,7 @@ impl<'a, T: 'static> UserDataMethods<'a, T> for UserDataRegistry<'a, T> { where F: Fn(&'a Lua, A) -> FR + MaybeSend + 'static, A: FromLuaMulti, - FR: Future> + 'a, + FR: Future> + MaybeSend + 'a, R: IntoLuaMulti, { let name = name.to_string(); @@ -480,7 +477,7 @@ impl<'a, T: 'static> UserDataMethods<'a, T> for UserDataRegistry<'a, T> { where M: Fn(&'a Lua, &'a T, A) -> MR + MaybeSend + 'static, A: FromLuaMulti, - MR: Future> + 'a, + MR: Future> + MaybeSend + 'a, R: IntoLuaMulti, { let name = name.to_string(); @@ -493,7 +490,7 @@ impl<'a, T: 'static> UserDataMethods<'a, T> for UserDataRegistry<'a, T> { where M: Fn(&'a Lua, &'a mut T, A) -> MR + MaybeSend + 'static, A: FromLuaMulti, - MR: Future> + 'a, + MR: Future> + MaybeSend + 'a, R: IntoLuaMulti, { let name = name.to_string(); @@ -528,7 +525,7 @@ impl<'a, T: 'static> UserDataMethods<'a, T> for UserDataRegistry<'a, T> { where F: Fn(&'a Lua, A) -> FR + MaybeSend + 'static, A: FromLuaMulti, - FR: Future> + 'a, + FR: Future> + MaybeSend + 'a, R: IntoLuaMulti, { let name = name.to_string(); diff --git a/tests/async.rs b/tests/async.rs index 871ebf0..038d14f 100644 --- a/tests/async.rs +++ b/tests/async.rs @@ -1,9 +1,10 @@ #![cfg(feature = "async")] -use std::sync::{Arc, Mutex}; +use std::sync::Arc; use std::time::Duration; use futures_util::stream::TryStreamExt; +use tokio::sync::Mutex; use mlua::{ AnyUserDataExt, Error, Function, Lua, LuaOptions, MultiValue, Result, StdLib, Table, TableExt, UserData, @@ -504,7 +505,7 @@ async fn test_async_terminate() -> Result<()> { let func = lua.create_async_function(move |_, ()| { let mutex = mutex2.clone(); async move { - let _guard = mutex.lock(); + let _guard = mutex.lock().await; sleep_ms(100).await; Ok(()) }