diff --git a/examples/async_http_server.rs b/examples/async_http_server.rs index 43ae7a9..337eebb 100644 --- a/examples/async_http_server.rs +++ b/examples/async_http_server.rs @@ -1,7 +1,6 @@ use std::future::Future; use std::net::SocketAddr; use std::pin::Pin; -use std::rc::Rc; use std::task::{Context, Poll}; use hyper::server::conn::AddrStream; @@ -9,7 +8,8 @@ use hyper::service::Service; use hyper::{Body, Request, Response, Server}; use mlua::{ - chunk, Error as LuaError, Function, Lua, String as LuaString, Table, UserData, UserDataMethods, + chunk, Error as LuaError, Function, Lua, OwnedFunction, String as LuaString, Table, UserData, + UserDataMethods, }; struct LuaRequest(SocketAddr, Request); @@ -21,7 +21,10 @@ impl UserData for LuaRequest { } } -pub struct Svc(Rc, SocketAddr); +#[derive(Clone)] +struct Handler(OwnedFunction); + +pub struct Svc(Handler, SocketAddr); impl Service> for Svc { type Response = Response; @@ -34,11 +37,10 @@ impl Service> for Svc { fn call(&mut self, req: Request) -> Self::Future { // If handler returns an error then generate 5xx response - let lua = self.0.clone(); + let handler = self.0.clone(); let lua_req = LuaRequest(self.1, req); Box::pin(async move { - let handler: Function = lua.named_registry_value("http_handler")?; - match handler.call_async::<_, Table>(lua_req).await { + match handler.0.to_ref().call_async::<_, Table>(lua_req).await { Ok(lua_resp) => { let status = lua_resp.get::<_, Option>("status")?.unwrap_or(200); let mut resp = Response::builder().status(status); @@ -72,10 +74,10 @@ impl Service> for Svc { #[tokio::main(flavor = "current_thread")] async fn main() { - let lua = Rc::new(Lua::new()); + let lua = Lua::new(); // Create Lua handler function - let handler: Function = lua + let handler_fn: Function = lua .load(chunk! { function(req) return { @@ -91,12 +93,10 @@ async fn main() { .eval() .expect("cannot create Lua handler"); - // Store it in the Registry - lua.set_named_registry_value("http_handler", handler) - .expect("cannot store Lua handler"); - let addr = ([127, 0, 0, 1], 3000).into(); - let server = Server::bind(&addr).executor(LocalExec).serve(MakeSvc(lua)); + let server = Server::bind(&addr) + .executor(LocalExec) + .serve(MakeSvc(Handler(handler_fn.into_owned()))); println!("Listening on http://{}", addr); @@ -105,7 +105,7 @@ async fn main() { local.run_until(server).await.expect("cannot run server") } -struct MakeSvc(Rc); +struct MakeSvc(Handler); impl Service<&AddrStream> for MakeSvc { type Response = Svc; @@ -117,9 +117,9 @@ impl Service<&AddrStream> for MakeSvc { } fn call(&mut self, stream: &AddrStream) -> Self::Future { - let lua = self.0.clone(); + let handler = self.0.clone(); let remote_addr = stream.remote_addr(); - Box::pin(async move { Ok(Svc(lua, remote_addr)) }) + Box::pin(async move { Ok(Svc(handler, remote_addr)) }) } } diff --git a/src/conversion.rs b/src/conversion.rs index 59717f0..5e89649 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -11,13 +11,13 @@ use bstr::{BStr, BString}; use num_traits::cast; use crate::error::{Error, Result}; -use crate::function::Function; +use crate::function::{Function, OwnedFunction}; use crate::lua::Lua; -use crate::string::String; -use crate::table::Table; -use crate::thread::Thread; +use crate::string::{OwnedString, String}; +use crate::table::{OwnedTable, Table}; +use crate::thread::{OwnedThread, Thread}; use crate::types::{LightUserData, MaybeSend}; -use crate::userdata::{AnyUserData, UserData}; +use crate::userdata::{AnyUserData, OwnedAnyUserData, UserData}; use crate::value::{FromLua, Nil, ToLua, Value}; impl<'lua> ToLua<'lua> for Value<'lua> { @@ -54,6 +54,20 @@ impl<'lua> FromLua<'lua> for String<'lua> { } } +impl<'lua> ToLua<'lua> for OwnedString { + #[inline] + fn to_lua(self, lua: &'lua Lua) -> Result> { + Ok(Value::String(String(lua.adopt_owned_ref(self.0)))) + } +} + +impl<'lua> FromLua<'lua> for OwnedString { + #[inline] + fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result { + String::from_lua(value, lua).map(|s| s.into_owned()) + } +} + impl<'lua> ToLua<'lua> for Table<'lua> { #[inline] fn to_lua(self, _: &'lua Lua) -> Result> { @@ -75,6 +89,20 @@ impl<'lua> FromLua<'lua> for Table<'lua> { } } +impl<'lua> ToLua<'lua> for OwnedTable { + #[inline] + fn to_lua(self, lua: &'lua Lua) -> Result> { + Ok(Value::Table(Table(lua.adopt_owned_ref(self.0)))) + } +} + +impl<'lua> FromLua<'lua> for OwnedTable { + #[inline] + fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result { + Table::from_lua(value, lua).map(|s| s.into_owned()) + } +} + impl<'lua> ToLua<'lua> for Function<'lua> { #[inline] fn to_lua(self, _: &'lua Lua) -> Result> { @@ -96,6 +124,20 @@ impl<'lua> FromLua<'lua> for Function<'lua> { } } +impl<'lua> ToLua<'lua> for OwnedFunction { + #[inline] + fn to_lua(self, lua: &'lua Lua) -> Result> { + Ok(Value::Function(Function(lua.adopt_owned_ref(self.0)))) + } +} + +impl<'lua> FromLua<'lua> for OwnedFunction { + #[inline] + fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result { + Function::from_lua(value, lua).map(|s| s.into_owned()) + } +} + impl<'lua> ToLua<'lua> for Thread<'lua> { #[inline] fn to_lua(self, _: &'lua Lua) -> Result> { @@ -117,6 +159,20 @@ impl<'lua> FromLua<'lua> for Thread<'lua> { } } +impl<'lua> ToLua<'lua> for OwnedThread { + #[inline] + fn to_lua(self, lua: &'lua Lua) -> Result> { + Ok(Value::Thread(Thread(lua.adopt_owned_ref(self.0)))) + } +} + +impl<'lua> FromLua<'lua> for OwnedThread { + #[inline] + fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result { + Thread::from_lua(value, lua).map(|s| s.into_owned()) + } +} + impl<'lua> ToLua<'lua> for AnyUserData<'lua> { #[inline] fn to_lua(self, _: &'lua Lua) -> Result> { @@ -138,6 +194,20 @@ impl<'lua> FromLua<'lua> for AnyUserData<'lua> { } } +impl<'lua> ToLua<'lua> for OwnedAnyUserData { + #[inline] + fn to_lua(self, lua: &'lua Lua) -> Result> { + Ok(Value::UserData(AnyUserData(lua.adopt_owned_ref(self.0)))) + } +} + +impl<'lua> FromLua<'lua> for OwnedAnyUserData { + #[inline] + fn from_lua(value: Value<'lua>, lua: &'lua Lua) -> Result { + AnyUserData::from_lua(value, lua).map(|s| s.into_owned()) + } +} + impl<'lua, T: 'static + MaybeSend + UserData> ToLua<'lua> for T { #[inline] fn to_lua(self, lua: &'lua Lua) -> Result> { @@ -145,6 +215,7 @@ impl<'lua, T: 'static + MaybeSend + UserData> ToLua<'lua> for T { } } +// TODO: Remove impl<'lua, T: 'static + UserData + Clone> FromLua<'lua> for T { #[inline] fn from_lua(value: Value<'lua>, _: &'lua Lua) -> Result { diff --git a/src/function.rs b/src/function.rs index 8fb8d28..a981007 100644 --- a/src/function.rs +++ b/src/function.rs @@ -5,7 +5,7 @@ use std::slice; use crate::error::{Error, Result}; use crate::ffi; -use crate::types::LuaRef; +use crate::types::{LuaOwnedRef, LuaRef}; use crate::util::{ assert_stack, check_stack, error_traceback, pop_error, ptr_to_cstr_bytes, StackGuard, }; @@ -18,6 +18,17 @@ use {futures_core::future::LocalBoxFuture, futures_util::future}; #[derive(Clone, Debug)] pub struct Function<'lua>(pub(crate) LuaRef<'lua>); +/// Owned handle to an internal Lua function. +#[derive(Clone, Debug)] +pub struct OwnedFunction(pub(crate) LuaOwnedRef); + +impl OwnedFunction { + /// Get borrowed handle to the underlying Lua function. + pub const fn to_ref(&self) -> Function { + Function(self.0.to_ref()) + } +} + #[derive(Clone, Debug)] pub struct FunctionInfo { pub name: Option>, @@ -373,6 +384,12 @@ impl<'lua> Function<'lua> { ffi::lua_getcoverage(lua.state, -1, func_ptr, callback::); } } + + /// Convert this handle to owned version. + #[inline] + pub fn into_owned(self) -> OwnedFunction { + OwnedFunction(self.0.into_owned()) + } } impl<'lua> PartialEq for Function<'lua> { diff --git a/src/lib.rs b/src/lib.rs index 24208b8..3dd3ca6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -108,15 +108,15 @@ pub use crate::{ffi::lua_CFunction, ffi::lua_State}; pub use crate::chunk::{AsChunk, Chunk, ChunkMode}; pub use crate::error::{Error, ExternalError, ExternalResult, Result}; -pub use crate::function::{Function, FunctionInfo}; +pub use crate::function::{Function, FunctionInfo, OwnedFunction}; pub use crate::hook::{Debug, DebugEvent, DebugNames, DebugSource, DebugStack}; pub use crate::lua::{GCMode, Lua, LuaOptions}; pub use crate::multi::Variadic; pub use crate::scope::Scope; pub use crate::stdlib::StdLib; -pub use crate::string::String; -pub use crate::table::{Table, TableExt, TablePairs, TableSequence}; -pub use crate::thread::{Thread, ThreadStatus}; +pub use crate::string::{OwnedString, String}; +pub use crate::table::{OwnedTable, Table, TableExt, TablePairs, TableSequence}; +pub use crate::thread::{OwnedThread, Thread, ThreadStatus}; pub use crate::types::{Integer, LightUserData, Number, RegistryKey}; pub use crate::userdata::{ AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMetatable, UserDataMethods, diff --git a/src/lua.rs b/src/lua.rs index d1001de..4820dc7 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -25,8 +25,8 @@ use crate::string::String; use crate::table::Table; use crate::thread::Thread; use crate::types::{ - Callback, CallbackUpvalue, DestructedUserdata, Integer, LightUserData, LuaRef, MaybeSend, - Number, RegistryKey, + Callback, CallbackUpvalue, DestructedUserdata, Integer, LightUserData, LuaOwnedRef, LuaRef, + MaybeSend, Number, RegistryKey, }; use crate::userdata::{AnyUserData, UserData, UserDataCell}; use crate::userdata_impl::{StaticUserDataFields, StaticUserDataMethods, UserDataProxy}; @@ -1721,7 +1721,7 @@ impl Lua { ffi::lua_replace(thread_state, ffi::LUA_GLOBALSINDEX); } - return Ok(Thread(LuaRef { lua: self, index })); + return Ok(Thread(LuaRef::new(self, index))); } }; self.create_thread(func) @@ -1750,7 +1750,7 @@ impl Lua { #[cfg(feature = "luau")] ffi::lua_resetthread(thread_state); extra.recycled_thread_cache.push(thread.0.index); - thread.0.index = 0; + thread.0.drop = false; return true; } false @@ -2440,7 +2440,7 @@ impl Lua { // Pushes a LuaRef value onto the stack, uses 1 stack space, does not call checkstack pub(crate) unsafe fn push_ref(&self, lref: &LuaRef) { assert!( - Arc::ptr_eq(&lref.lua.extra, &self.extra), + Arc::ptr_eq(&lref.lua.0, &self.0), "Lua instance passed Value created from a different main Lua state" ); let extra = &*self.extra.get(); @@ -2460,14 +2460,14 @@ impl Lua { let extra = &mut *self.extra.get(); ffi::lua_xmove(self.state, extra.ref_thread, 1); let index = ref_stack_pop(extra); - LuaRef { lua: self, index } + LuaRef::new(self, index) } // Same as `pop_ref` but assumes the value is already on the reference thread pub(crate) unsafe fn pop_ref_thread(&self) -> LuaRef { let extra = &mut *self.extra.get(); let index = ref_stack_pop(extra); - LuaRef { lua: self, index } + LuaRef::new(self, index) } pub(crate) fn clone_ref<'lua>(&'lua self, lref: &LuaRef<'lua>) -> LuaRef<'lua> { @@ -2475,7 +2475,7 @@ impl Lua { let extra = &mut *self.extra.get(); ffi::lua_pushvalue(extra.ref_thread, lref.index); let index = ref_stack_pop(extra); - LuaRef { lua: self, index } + LuaRef::new(self, index) } } @@ -2488,6 +2488,29 @@ impl Lua { } } + pub(crate) fn make_owned_ref(&self, lref: LuaRef) -> LuaOwnedRef { + assert!(lref.drop, "Cannot make owned non-drop reference"); + let owned_ref = LuaOwnedRef { + lua: Lua(self.0.clone()), + index: lref.index, + }; + mem::forget(lref); + owned_ref + } + + pub(crate) fn adopt_owned_ref(&self, loref: LuaOwnedRef) -> LuaRef { + assert!( + Arc::ptr_eq(&loref.lua.0, &self.0), + "Lua instance passed Value created from a different main Lua state" + ); + let index = loref.index; + unsafe { + ptr::read(&loref.lua); + mem::forget(loref); + } + LuaRef::new(self, index) + } + unsafe fn push_userdata_metatable(&self) -> Result<()> { let extra = &mut *self.extra.get(); diff --git a/src/string.rs b/src/string.rs index 7ea251a..60d7072 100644 --- a/src/string.rs +++ b/src/string.rs @@ -12,7 +12,7 @@ use { use crate::error::{Error, Result}; use crate::ffi; -use crate::types::LuaRef; +use crate::types::{LuaOwnedRef, LuaRef}; /// Handle to an internal Lua string. /// @@ -20,6 +20,17 @@ use crate::types::LuaRef; #[derive(Clone, Debug)] pub struct String<'lua>(pub(crate) LuaRef<'lua>); +/// Owned handle to an internal Lua string. +#[derive(Clone, Debug)] +pub struct OwnedString(pub(crate) LuaOwnedRef); + +impl OwnedString { + /// Get borrowed handle to the underlying Lua string. + pub const fn to_ref(&self) -> String { + String(self.0.to_ref()) + } +} + impl<'lua> String<'lua> { /// Get a `&str` slice if the Lua string is valid UTF-8. /// @@ -122,6 +133,12 @@ impl<'lua> String<'lua> { let ref_thread = self.0.lua.ref_thread(); unsafe { ffi::lua_topointer(ref_thread, self.0.index) } } + + /// Convert this handle to owned version. + #[inline] + pub fn into_owned(self) -> OwnedString { + OwnedString(self.0.into_owned()) + } } impl<'lua> AsRef<[u8]> for String<'lua> { diff --git a/src/table.rs b/src/table.rs index 424aaf0..2d9a889 100644 --- a/src/table.rs +++ b/src/table.rs @@ -11,7 +11,7 @@ use { use crate::error::{Error, Result}; use crate::ffi; use crate::function::Function; -use crate::types::{Integer, LuaRef}; +use crate::types::{Integer, LuaOwnedRef, LuaRef}; use crate::util::{assert_stack, check_stack, StackGuard}; use crate::value::{FromLua, FromLuaMulti, Nil, ToLua, ToLuaMulti, Value}; @@ -22,6 +22,17 @@ use {futures_core::future::LocalBoxFuture, futures_util::future}; #[derive(Clone, Debug)] pub struct Table<'lua>(pub(crate) LuaRef<'lua>); +/// Owned handle to an internal Lua table. +#[derive(Clone, Debug)] +pub struct OwnedTable(pub(crate) LuaOwnedRef); + +impl OwnedTable { + /// Get borrowed handle to the underlying Lua table. + pub const fn to_ref(&self) -> Table { + Table(self.0.to_ref()) + } +} + #[allow(clippy::len_without_is_empty)] impl<'lua> Table<'lua> { /// Sets a key-value pair in the table. @@ -512,6 +523,12 @@ impl<'lua> Table<'lua> { unsafe { ffi::lua_topointer(ref_thread, self.0.index) } } + /// Convert this handle to owned version. + #[inline] + pub fn into_owned(self) -> OwnedTable { + OwnedTable(self.0.into_owned()) + } + /// Consume this table and return an iterator over the pairs of the table. /// /// This works like the Lua `pairs` function, but does not invoke the `__pairs` metamethod. diff --git a/src/thread.rs b/src/thread.rs index c0a4260..811c056 100644 --- a/src/thread.rs +++ b/src/thread.rs @@ -3,7 +3,7 @@ use std::os::raw::c_int; use crate::error::{Error, Result}; use crate::ffi; -use crate::types::LuaRef; +use crate::types::{LuaOwnedRef, LuaRef}; use crate::util::{check_stack, error_traceback_thread, pop_error, StackGuard}; use crate::value::{FromLuaMulti, ToLuaMulti}; @@ -48,6 +48,17 @@ pub enum ThreadStatus { #[derive(Clone, Debug)] pub struct Thread<'lua>(pub(crate) LuaRef<'lua>); +/// Owned handle to an internal Lua thread. +#[derive(Clone, Debug)] +pub struct OwnedThread(pub(crate) LuaOwnedRef); + +impl OwnedThread { + /// Get borrowed handle to the underlying Lua thread. + pub const fn to_ref(&self) -> Thread { + Thread(self.0.to_ref()) + } +} + /// Thread (coroutine) representation as an async [`Future`] or [`Stream`]. /// /// Requires `feature = "async"` @@ -333,6 +344,12 @@ impl<'lua> Thread<'lua> { protect_lua!(lua.state, 0, 0, |_| ffi::luaL_sandboxthread(thread)) } } + + /// Convert this handle to owned version. + #[inline] + pub fn into_owned(self) -> OwnedThread { + OwnedThread(self.0.into_owned()) + } } impl<'lua> PartialEq for Thread<'lua> { diff --git a/src/types.rs b/src/types.rs index 9404d10..d5a5413 100644 --- a/src/types.rs +++ b/src/types.rs @@ -180,6 +180,21 @@ impl RegistryKey { pub(crate) struct LuaRef<'lua> { pub(crate) lua: &'lua Lua, pub(crate) index: c_int, + pub(crate) drop: bool, +} + +impl<'lua> LuaRef<'lua> { + pub(crate) const fn new(lua: &'lua Lua, index: c_int) -> Self { + LuaRef { + lua, + index, + drop: true, + } + } + + pub(crate) fn into_owned(self) -> LuaOwnedRef { + self.lua.make_owned_ref(self) + } } impl<'lua> fmt::Debug for LuaRef<'lua> { @@ -196,7 +211,7 @@ impl<'lua> Clone for LuaRef<'lua> { impl<'lua> Drop for LuaRef<'lua> { fn drop(&mut self) { - if self.index > 0 { + if self.drop { self.lua.drop_ref(self); } } @@ -214,3 +229,40 @@ impl<'lua> PartialEq for LuaRef<'lua> { } } } + +pub(crate) struct LuaOwnedRef { + pub(crate) lua: Lua, + pub(crate) index: c_int, +} + +impl fmt::Debug for LuaOwnedRef { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "OwnedRef({})", self.index) + } +} + +impl Clone for LuaOwnedRef { + fn clone(&self) -> Self { + self.lua.make_owned_ref(self.to_ref().clone()) + } +} + +impl Drop for LuaOwnedRef { + fn drop(&mut self) { + self.lua.drop_ref(&LuaRef { + lua: &self.lua, + index: self.index, + drop: false, + }); + } +} + +impl LuaOwnedRef { + pub(crate) const fn to_ref(&self) -> LuaRef { + LuaRef { + lua: &self.lua, + index: self.index, + drop: false, + } + } +} diff --git a/src/userdata.rs b/src/userdata.rs index 0ad5f46..af5b304 100644 --- a/src/userdata.rs +++ b/src/userdata.rs @@ -20,7 +20,7 @@ use crate::ffi; use crate::function::Function; use crate::lua::Lua; use crate::table::{Table, TablePairs}; -use crate::types::{Callback, LuaRef, MaybeSend}; +use crate::types::{Callback, LuaOwnedRef, LuaRef, MaybeSend}; use crate::util::{check_stack, get_userdata, take_userdata, StackGuard}; use crate::value::{FromLua, FromLuaMulti, ToLua, ToLuaMulti}; @@ -799,6 +799,15 @@ impl Serialize for UserDataSerializeError { #[derive(Clone, Debug)] pub struct AnyUserData<'lua>(pub(crate) LuaRef<'lua>); +#[derive(Clone, Debug)] +pub struct OwnedAnyUserData(pub(crate) LuaOwnedRef); + +impl OwnedAnyUserData { + pub const fn to_ref(&self) -> AnyUserData { + AnyUserData(self.0.to_ref()) + } +} + impl<'lua> AnyUserData<'lua> { /// Checks whether the type of this userdata is `T`. pub fn is(&self) -> bool { @@ -1090,6 +1099,11 @@ impl<'lua> AnyUserData<'lua> { } } + #[inline] + pub fn into_owned(self) -> OwnedAnyUserData { + OwnedAnyUserData(self.0.into_owned()) + } + pub(crate) fn equals>(&self, other: T) -> Result { let other = other.as_ref(); // Uses lua_rawequal() under the hood