From 6e2bb73cff65729118841f338577bfec14d593ec Mon Sep 17 00:00:00 2001 From: Alex Orlenko Date: Tue, 5 May 2020 13:03:28 +0100 Subject: [PATCH] Bind Futures lifetimes to 'lua rather than 'static. Fix async examples. --- examples/async_tcp_server.rs | 34 ++++++++++++++++++++++++++++------ src/lua.rs | 36 ++++++++++++++++++++++++++++++------ src/userdata.rs | 4 ++-- 3 files changed, 60 insertions(+), 14 deletions(-) diff --git a/examples/async_tcp_server.rs b/examples/async_tcp_server.rs index d105fcd..4f75b4c 100644 --- a/examples/async_tcp_server.rs +++ b/examples/async_tcp_server.rs @@ -61,10 +61,7 @@ impl UserData for LuaTcpStream { } } -#[tokio::main] -async fn main() -> Result<()> { - let lua = Lua::new(); - +async fn run_server(lua: &'static Lua) -> Result<()> { let spawn = lua.create_function(move |_, func: Function| { task::spawn_local(async move { func.call_async::<_, ()>(()).await.unwrap() }); Ok(()) @@ -80,20 +77,33 @@ async fn main() -> Result<()> { local addr = ... local listener = tcp.bind(addr) print("listening on "..addr) + + local accept_new = true while true do local stream = listener:accept() local peer_addr = stream:peer_addr() print("connected from "..peer_addr) + + if not accept_new then + return + end + spawn(function() while true do local data = stream:read(100) data = data:match("^%s*(.-)%s*$") -- trim print("["..peer_addr.."] "..data) - stream:write("got: "..data.."\n") + if data == "bye" then + stream:write("bye bye\n") + stream:close() + return + end if data == "exit" then stream:close() - break + accept_new = false + return end + stream:write("echo: "..data.."\n") end end) end @@ -105,3 +115,15 @@ async fn main() -> Result<()> { .run_until(server.call_async::<_, ()>("0.0.0.0:1234")) .await } + +#[tokio::main] +async fn main() { + let lua = Lua::new().into_static(); + + run_server(lua).await.unwrap(); + + // Consume the static reference and drop it. + // This is safe as long as we don't hold any other references to Lua + // or alive resources. + unsafe { Lua::from_static(lua) }; +} diff --git a/src/lua.rs b/src/lua.rs index f9b23d7..b5b34d8 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -75,7 +75,7 @@ impl Drop for Lua { extra.registry_unref_list.try_borrow_mut(), "unref list borrowed" ) = None; - ffi::lua_close(self.state); + ffi::lua_close(self.main_state); } } } @@ -113,6 +113,30 @@ impl Lua { } } + /// Consumes and leaks `Lua` object, returning a static reference `&'static Lua`. + /// + /// This function is useful when the `Lua` object is supposed to live for the remainder + /// of the program's life. + /// In particular in asynchronous context this will allow to spawn Lua tasks to execute + /// in background. + /// + /// Dropping the returned reference will cause a memory leak. If this is not acceptable, + /// the reference should first be wrapped with the [`Lua::from_static`] function producing a `Lua`. + /// This `Lua` object can then be dropped which will properly release the allocated memory. + /// + /// [`Lua::from_static`]: #method.from_static + pub fn into_static(self) -> &'static Self { + Box::leak(Box::new(self)) + } + + /// Constructs a `Lua` from a static reference to it. + /// + /// # Safety + /// This function is unsafe because improper use may lead to memory problems or undefined behavior. + pub unsafe fn from_static(lua: &'static Lua) -> Self { + *Box::from_raw(lua as *const Lua as *mut Lua) + } + /// Loads the specified set of standard libraries into an existing Lua state. /// /// Use the [`StdLib`] flags to specifiy the libraries you want to load. @@ -529,7 +553,7 @@ impl Lua { A: FromLuaMulti<'callback>, R: ToLuaMulti<'callback>, F: 'static + Fn(&'callback Lua, A) -> FR, - FR: 'static + Future>, + FR: 'lua + Future>, { self.create_async_callback(Box::new(move |lua, args| { let args = match A::from_lua_multi(args, lua) { @@ -1614,7 +1638,7 @@ impl<'lua, T: 'static + UserData> UserDataMethods<'lua, T> for StaticUserDataMet A: FromLuaMulti<'lua>, R: ToLuaMulti<'lua>, M: 'static + Fn(&'lua Lua, T, A) -> MR, - MR: 'static + Future>, + MR: 'lua + Future>, { self.async_methods .push((name.as_ref().to_vec(), Self::box_async_method(method))); @@ -1650,7 +1674,7 @@ impl<'lua, T: 'static + UserData> UserDataMethods<'lua, T> for StaticUserDataMet A: FromLuaMulti<'lua>, R: ToLuaMulti<'lua>, F: 'static + Fn(&'lua Lua, A) -> FR, - FR: 'static + Future>, + FR: 'lua + Future>, { self.async_methods .push((name.as_ref().to_vec(), Self::box_async_function(function))); @@ -1748,7 +1772,7 @@ impl<'lua, T: 'static + UserData> StaticUserDataMethods<'lua, T> { A: FromLuaMulti<'lua>, R: ToLuaMulti<'lua>, M: 'static + Fn(&'lua Lua, T, A) -> MR, - MR: 'static + Future>, + MR: 'lua + Future>, { Box::new(move |lua, mut args| { let fut_res = || { @@ -1801,7 +1825,7 @@ impl<'lua, T: 'static + UserData> StaticUserDataMethods<'lua, T> { A: FromLuaMulti<'lua>, R: ToLuaMulti<'lua>, F: 'static + Fn(&'lua Lua, A) -> FR, - FR: 'static + Future>, + FR: 'lua + Future>, { Box::new(move |lua, args| { let args = match A::from_lua_multi(args, lua) { diff --git a/src/userdata.rs b/src/userdata.rs index fc6f86f..1b44ab7 100644 --- a/src/userdata.rs +++ b/src/userdata.rs @@ -165,7 +165,7 @@ pub trait UserDataMethods<'lua, T: UserData> { A: FromLuaMulti<'lua>, R: ToLuaMulti<'lua>, M: 'static + Fn(&'lua Lua, T, A) -> MR, - MR: 'static + Future>; + MR: 'lua + Future>; /// Add a regular method as a function which accepts generic arguments, the first argument will /// be a `UserData` of type T if the method is called with Lua method syntax: @@ -209,7 +209,7 @@ pub trait UserDataMethods<'lua, T: UserData> { A: FromLuaMulti<'lua>, R: ToLuaMulti<'lua>, F: 'static + Fn(&'lua Lua, A) -> FR, - FR: 'static + Future>; + FR: 'lua + Future>; /// Add a metamethod which accepts a `&T` as the first parameter. ///