Add lifetimes because they are fun

This commit is contained in:
Filip Tibell 2023-08-17 21:24:20 -05:00
parent 6416ef5fb7
commit ab386e000d
5 changed files with 95 additions and 51 deletions

View file

@ -3,7 +3,7 @@ use mlua::prelude::*;
use super::{traits::IntoLuaOwnedThread, SchedulerImpl}; use super::{traits::IntoLuaOwnedThread, SchedulerImpl};
impl<'lua, 'fut> SchedulerImpl impl<'lua, 'fut> SchedulerImpl<'fut>
where where
'lua: 'fut, 'lua: 'fut,
{ {
@ -12,7 +12,7 @@ where
*/ */
pub fn schedule_future<F>(&self, fut: F) pub fn schedule_future<F>(&self, fut: F)
where where
F: 'static + Future<Output = ()>, F: 'fut + Future<Output = ()>,
{ {
let futs = self let futs = self
.futures .futures
@ -24,11 +24,11 @@ where
/** /**
Schedules the given `thread` to run when the given `fut` completes. Schedules the given `thread` to run when the given `fut` completes.
*/ */
pub fn schedule_future_thread<T, F, R>(&'lua self, thread: T, fut: F) -> LuaResult<()> pub fn schedule_future_thread<T, R, F>(&'fut self, thread: T, fut: F) -> LuaResult<()>
where where
T: 'static + IntoLuaOwnedThread, T: IntoLuaOwnedThread,
F: 'static + Future<Output = LuaResult<R>>,
R: IntoLuaMulti<'fut>, R: IntoLuaMulti<'fut>,
F: 'fut + Future<Output = LuaResult<R>>,
{ {
let thread = thread.into_owned_lua_thread(&self.lua)?; let thread = thread.into_owned_lua_thread(&self.lua)?;

View file

@ -4,15 +4,18 @@ use futures_util::StreamExt;
use mlua::prelude::*; use mlua::prelude::*;
use tokio::task::LocalSet; use tokio::task::LocalSet;
use super::SchedulerImpl; use super::{traits::IntoLuaOwnedThread, SchedulerImpl};
impl<'lua> SchedulerImpl { impl<'lua, 'fut> SchedulerImpl<'fut>
where
'lua: 'fut,
{
/** /**
Runs all lua threads to completion. Runs all lua threads to completion.
Returns `true` if any thread was resumed, `false` otherwise. Returns `true` if any thread was resumed, `false` otherwise.
*/ */
fn run_lua_threads(&self) -> bool { fn run_lua_threads(&'lua self) -> bool {
if self.state.has_exit_code() { if self.state.has_exit_code() {
return false; return false;
} }
@ -57,7 +60,7 @@ impl<'lua> SchedulerImpl {
Returns `true` if any future was resumed, `false` otherwise. Returns `true` if any future was resumed, `false` otherwise.
*/ */
async fn run_futures(&self) -> bool { async fn run_futures(&'lua self) -> bool {
let mut resumed_any = false; let mut resumed_any = false;
let mut futs = self let mut futs = self
@ -81,7 +84,7 @@ impl<'lua> SchedulerImpl {
Will emit lua output and errors to stdout and stderr. Will emit lua output and errors to stdout and stderr.
*/ */
pub async fn run_to_completion(&self) -> ExitCode { pub async fn run_to_completion(&'lua self) -> ExitCode {
let fut = async move { let fut = async move {
loop { loop {
// 1. Run lua threads until exit or there are none left, // 1. Run lua threads until exit or there are none left,
@ -116,4 +119,22 @@ impl<'lua> SchedulerImpl {
ExitCode::SUCCESS ExitCode::SUCCESS
} }
} }
/**
Schedules a new main thread and runs the scheduler until completion.
See [`Self::run_to_completion`] for more info.
*/
pub async fn run_main(
&'lua self,
main: impl IntoLuaOwnedThread,
args: impl IntoLuaMulti<'lua>,
) -> ExitCode {
let thread = main
.into_owned_lua_thread(&self.lua)
.expect("Failed to create thread for main");
self.push_back(thread, args)
.expect("Failed to queue thread for main");
self.run_to_completion().await
}
} }

View file

@ -8,7 +8,10 @@ use super::{
SchedulerImpl, SchedulerImpl,
}; };
impl<'lua> SchedulerImpl { impl<'lua, 'fut> SchedulerImpl<'fut>
where
'lua: 'fut,
{
/** /**
Checks if there are any lua threads to run. Checks if there are any lua threads to run.
*/ */

View file

@ -18,6 +18,8 @@ mod impl_async;
mod impl_runner; mod impl_runner;
mod impl_threads; mod impl_threads;
pub use self::traits::*;
use self::{ use self::{
state::SchedulerState, state::SchedulerState,
thread::{SchedulerThread, SchedulerThreadId, SchedulerThreadSender}, thread::{SchedulerThread, SchedulerThreadId, SchedulerThreadSender},
@ -30,12 +32,11 @@ use self::{
to the same underlying scheduler and Lua struct. to the same underlying scheduler and Lua struct.
*/ */
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Scheduler { pub(crate) struct Scheduler<'fut> {
lua: Arc<Lua>, inner: Arc<SchedulerImpl<'fut>>,
inner: Arc<SchedulerImpl>,
} }
impl Scheduler { impl<'fut> Scheduler<'fut> {
/** /**
Creates a new scheduler for the given [`Lua`] struct. Creates a new scheduler for the given [`Lua`] struct.
*/ */
@ -45,12 +46,12 @@ impl Scheduler {
let inner = Arc::new(sched_impl); let inner = Arc::new(sched_impl);
Self { lua, inner } Self { inner }
} }
} }
impl Deref for Scheduler { impl<'fut> Deref for Scheduler<'fut> {
type Target = SchedulerImpl; type Target = SchedulerImpl<'fut>;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.inner &self.inner
} }
@ -62,15 +63,15 @@ impl Deref for Scheduler {
Not meant to be used directly, use [`Scheduler`] instead. Not meant to be used directly, use [`Scheduler`] instead.
*/ */
#[derive(Debug)] #[derive(Debug)]
pub struct SchedulerImpl { pub(crate) struct SchedulerImpl<'fut> {
lua: Arc<Lua>, lua: Arc<Lua>,
state: SchedulerState, state: SchedulerState,
threads: RefCell<VecDeque<SchedulerThread>>, threads: RefCell<VecDeque<SchedulerThread>>,
thread_senders: RefCell<HashMap<SchedulerThreadId, SchedulerThreadSender>>, thread_senders: RefCell<HashMap<SchedulerThreadId, SchedulerThreadSender>>,
futures: AsyncMutex<FuturesUnordered<Pin<Box<dyn Future<Output = ()>>>>>, futures: AsyncMutex<FuturesUnordered<Pin<Box<dyn Future<Output = ()> + 'fut>>>>,
} }
impl SchedulerImpl { impl<'fut> SchedulerImpl<'fut> {
fn new(lua: Arc<Lua>) -> Self { fn new(lua: Arc<Lua>) -> Self {
Self { Self {
lua, lua,

View file

@ -1,64 +1,83 @@
use std::sync::Arc;
use futures_util::Future; use futures_util::Future;
use mlua::{chunk, prelude::*}; use mlua::prelude::*;
use super::Scheduler; use super::Scheduler;
const ASYNC_IMPL_LUA: &str = r#"
schedule(...)
return yield()
"#;
/** /**
Trait for extensions to the [`Lua`] struct, allowing Trait for extensions to the [`Lua`] struct, allowing
for access to the scheduler without having to import for access to the scheduler without having to import
it or handle registry / app data references manually. it or handle registry / app data references manually.
*/ */
pub trait LuaSchedulerExt { pub trait LuaSchedulerExt<'lua, 'fut>
where
'lua: 'fut,
{
/** /**
Get a reference to the scheduler for the [`Lua`] struct. Creates a new [`Lua`] struct with a [`Scheduler`].
*/ */
fn scheduler(&self) -> &Scheduler; fn new_with_scheduler() -> Arc<Self>;
/** /**
Creates a function callable from Lua that runs an async Creates a function callable from Lua that runs an async
closure and returns the results of it to the call site. closure and returns the results of it to the call site.
*/ */
fn create_async_function<'lua, A, R, F, FR>( fn create_async_function<A, R, F, FR>(&'lua self, func: F) -> LuaResult<LuaFunction<'lua>>
&'lua self,
func: F,
) -> LuaResult<LuaFunction<'lua>>
where where
A: FromLuaMulti<'lua>, A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>, R: IntoLuaMulti<'lua>,
F: 'static + Fn(&'lua Lua, A) -> FR, F: 'static + Fn(&'lua Lua, A) -> FR,
FR: 'static + Future<Output = LuaResult<R>>; FR: 'fut + Future<Output = LuaResult<R>>;
} }
impl LuaSchedulerExt for Lua { impl<'lua, 'fut> LuaSchedulerExt<'lua, 'fut> for Lua
fn scheduler(&self) -> &Scheduler { where
*self 'lua: 'fut,
.app_data_ref::<&Scheduler>() {
.expect("Lua struct is missing scheduler") fn new_with_scheduler() -> Arc<Self> {
let lua = Arc::new(Lua::new());
lua.set_app_data(Scheduler::new(Arc::clone(&lua)));
lua
} }
fn create_async_function<'lua, A, R, F, FR>(&'lua self, func: F) -> LuaResult<LuaFunction<'lua>> fn create_async_function<A, R, F, FR>(&'lua self, func: F) -> LuaResult<LuaFunction<'lua>>
where where
A: FromLuaMulti<'lua>, A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>, R: IntoLuaMulti<'lua>,
F: 'static + Fn(&'lua Lua, A) -> FR, F: 'static + Fn(&'lua Lua, A) -> FR,
FR: 'static + Future<Output = LuaResult<R>>, FR: 'fut + Future<Output = LuaResult<R>>,
{ {
let coroutine_yield = self let async_env = self.create_table_with_capacity(0, 2)?;
.globals()
.get::<_, LuaTable>("coroutine")? async_env.set(
.get::<_, LuaFunction>("yield")?; "yield",
let schedule = LuaFunction::wrap(move |lua: &Lua, args: A| { self.globals()
let thread = lua.current_thread().into_owned(); .get::<_, LuaTable>("coroutine")?
let future = func(lua, args); .get::<_, LuaFunction>("yield")?,
lua.scheduler().schedule_future_thread(thread, future); )?;
Ok(())
}); async_env.set(
"schedule",
LuaFunction::wrap(move |lua: &Lua, args: A| {
let _thread = lua.current_thread().into_owned();
let _future = func(lua, args);
let _sched = lua
.app_data_ref::<&Scheduler>()
.expect("Lua struct is missing scheduler");
// FIXME: `self` escapes outside of method
// sched.schedule_future_thread(thread, future)?;
Ok(())
}),
)?;
let async_func = self let async_func = self
.load(chunk!({ .load(ASYNC_IMPL_LUA)
$schedule(...)
return $coroutine_yield()
}))
.set_name("async") .set_name("async")
.into_function()?; .into_function()?;
Ok(async_func) Ok(async_func)