2023-02-16 22:23:52 +00:00
|
|
|
use async_trait::async_trait;
|
|
|
|
use futures_util::Future;
|
|
|
|
use mlua::prelude::*;
|
|
|
|
|
|
|
|
use crate::{lua::task::TaskScheduler, utils::table::TableBuilder};
|
|
|
|
|
2023-02-17 18:20:17 +00:00
|
|
|
use super::task::TaskSchedulerAsyncExt;
|
|
|
|
|
2023-02-20 12:02:22 +00:00
|
|
|
const ASYNC_IMPL_LUA: &str = r#"
|
|
|
|
resumeAsync(thread(), ...)
|
|
|
|
return yield()
|
|
|
|
"#;
|
|
|
|
|
|
|
|
const WAIT_IMPL_LUA: &str = r#"
|
|
|
|
resumeAfter(...)
|
|
|
|
return yield()
|
|
|
|
"#;
|
|
|
|
|
2023-02-16 22:23:52 +00:00
|
|
|
#[async_trait(?Send)]
|
|
|
|
pub trait LuaAsyncExt {
|
|
|
|
fn create_async_function<'lua, A, R, F, FR>(self, func: F) -> LuaResult<LuaFunction<'lua>>
|
|
|
|
where
|
|
|
|
A: FromLuaMulti<'static>,
|
|
|
|
R: ToLuaMulti<'static>,
|
|
|
|
F: 'static + Fn(&'static Lua, A) -> FR,
|
|
|
|
FR: 'static + Future<Output = LuaResult<R>>;
|
2023-02-17 18:20:17 +00:00
|
|
|
|
|
|
|
fn create_waiter_function<'lua>(self) -> LuaResult<LuaFunction<'lua>>;
|
2023-02-16 22:23:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl LuaAsyncExt for &'static Lua {
|
|
|
|
/**
|
|
|
|
Creates a function callable from Lua that runs an async
|
|
|
|
closure and returns the results of it to the call site.
|
|
|
|
*/
|
|
|
|
fn create_async_function<'lua, A, R, F, FR>(self, func: F) -> LuaResult<LuaFunction<'lua>>
|
|
|
|
where
|
|
|
|
A: FromLuaMulti<'static>,
|
|
|
|
R: ToLuaMulti<'static>,
|
|
|
|
F: 'static + Fn(&'static Lua, A) -> FR,
|
|
|
|
FR: 'static + Future<Output = LuaResult<R>>,
|
|
|
|
{
|
|
|
|
let async_env_yield: LuaFunction = self.named_registry_value("co.yield")?;
|
|
|
|
let async_env = TableBuilder::new(self)?
|
|
|
|
.with_value("yield", async_env_yield)?
|
2023-02-17 18:20:17 +00:00
|
|
|
.with_function("thread", |lua, _: ()| Ok(lua.current_thread()))?
|
2023-02-16 22:23:52 +00:00
|
|
|
.with_function(
|
|
|
|
"resumeAsync",
|
|
|
|
move |lua: &Lua, (thread, args): (LuaThread, A)| {
|
|
|
|
let fut = func(lua, args);
|
|
|
|
let sched = lua
|
|
|
|
.app_data_ref::<&TaskScheduler>()
|
|
|
|
.expect("Missing task scheduler as a lua app data");
|
2023-02-17 18:20:17 +00:00
|
|
|
sched.queue_async_task(thread, None, async {
|
2023-02-16 22:23:52 +00:00
|
|
|
let rets = fut.await?;
|
|
|
|
let mult = rets.to_lua_multi(lua)?;
|
|
|
|
Ok(Some(mult))
|
|
|
|
})
|
|
|
|
},
|
|
|
|
)?
|
|
|
|
.build_readonly()?;
|
|
|
|
let async_func = self
|
2023-02-20 12:02:22 +00:00
|
|
|
.load(ASYNC_IMPL_LUA)
|
2023-02-17 18:20:17 +00:00
|
|
|
.set_name("async")?
|
|
|
|
.set_environment(async_env)?
|
|
|
|
.into_function()?;
|
|
|
|
Ok(async_func)
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
Creates a special async function that waits the
|
|
|
|
desired amount of time, inheriting the guid of the
|
|
|
|
current thread / task for proper cancellation.
|
|
|
|
*/
|
|
|
|
fn create_waiter_function<'lua>(self) -> LuaResult<LuaFunction<'lua>> {
|
|
|
|
let async_env_yield: LuaFunction = self.named_registry_value("co.yield")?;
|
|
|
|
let async_env = TableBuilder::new(self)?
|
|
|
|
.with_value("yield", async_env_yield)?
|
|
|
|
.with_function("resumeAfter", move |lua: &Lua, duration: Option<f64>| {
|
|
|
|
let sched = lua
|
|
|
|
.app_data_ref::<&TaskScheduler>()
|
|
|
|
.expect("Missing task scheduler as a lua app data");
|
|
|
|
sched.schedule_wait(lua.current_thread(), duration)
|
|
|
|
})?
|
|
|
|
.build_readonly()?;
|
|
|
|
let async_func = self
|
2023-02-20 12:02:22 +00:00
|
|
|
.load(WAIT_IMPL_LUA)
|
2023-02-17 18:20:17 +00:00
|
|
|
.set_name("wait")?
|
2023-02-16 22:23:52 +00:00
|
|
|
.set_environment(async_env)?
|
|
|
|
.into_function()?;
|
|
|
|
Ok(async_func)
|
|
|
|
}
|
|
|
|
}
|