Bind Futures lifetimes to 'lua rather than 'static.

Fix async examples.
This commit is contained in:
Alex Orlenko 2020-05-05 13:03:28 +01:00
parent 7efcee853d
commit 6e2bb73cff
3 changed files with 60 additions and 14 deletions

View File

@ -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) };
}

View File

@ -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<Output = Result<R>>,
FR: 'lua + Future<Output = Result<R>>,
{
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<Output = Result<R>>,
MR: 'lua + Future<Output = Result<R>>,
{
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<Output = Result<R>>,
FR: 'lua + Future<Output = Result<R>>,
{
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<Output = Result<R>>,
MR: 'lua + Future<Output = Result<R>>,
{
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<Output = Result<R>>,
FR: 'lua + Future<Output = Result<R>>,
{
Box::new(move |lua, args| {
let args = match A::from_lua_multi(args, lua) {

View File

@ -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<Output = Result<R>>;
MR: 'lua + Future<Output = Result<R>>;
/// 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<Output = Result<R>>;
FR: 'lua + Future<Output = Result<R>>;
/// Add a metamethod which accepts a `&T` as the first parameter.
///