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};
impl<'lua, 'fut> SchedulerImpl
impl<'lua, 'fut> SchedulerImpl<'fut>
where
'lua: 'fut,
{
@ -12,7 +12,7 @@ where
*/
pub fn schedule_future<F>(&self, fut: F)
where
F: 'static + Future<Output = ()>,
F: 'fut + Future<Output = ()>,
{
let futs = self
.futures
@ -24,11 +24,11 @@ where
/**
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
T: 'static + IntoLuaOwnedThread,
F: 'static + Future<Output = LuaResult<R>>,
T: IntoLuaOwnedThread,
R: IntoLuaMulti<'fut>,
F: 'fut + Future<Output = LuaResult<R>>,
{
let thread = thread.into_owned_lua_thread(&self.lua)?;

View file

@ -4,15 +4,18 @@ use futures_util::StreamExt;
use mlua::prelude::*;
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.
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() {
return false;
}
@ -57,7 +60,7 @@ impl<'lua> SchedulerImpl {
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 futs = self
@ -81,7 +84,7 @@ impl<'lua> SchedulerImpl {
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 {
loop {
// 1. Run lua threads until exit or there are none left,
@ -116,4 +119,22 @@ impl<'lua> SchedulerImpl {
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,
};
impl<'lua> SchedulerImpl {
impl<'lua, 'fut> SchedulerImpl<'fut>
where
'lua: 'fut,
{
/**
Checks if there are any lua threads to run.
*/

View file

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

View file

@ -1,64 +1,83 @@
use std::sync::Arc;
use futures_util::Future;
use mlua::{chunk, prelude::*};
use mlua::prelude::*;
use super::Scheduler;
const ASYNC_IMPL_LUA: &str = r#"
schedule(...)
return yield()
"#;
/**
Trait for extensions to the [`Lua`] struct, allowing
for access to the scheduler without having to import
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
closure and returns the results of it to the call site.
*/
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
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
F: 'static + Fn(&'lua Lua, A) -> FR,
FR: 'static + Future<Output = LuaResult<R>>;
FR: 'fut + Future<Output = LuaResult<R>>;
}
impl LuaSchedulerExt for Lua {
fn scheduler(&self) -> &Scheduler {
*self
.app_data_ref::<&Scheduler>()
.expect("Lua struct is missing scheduler")
}
fn create_async_function<'lua, A, R, F, FR>(&'lua self, func: F) -> LuaResult<LuaFunction<'lua>>
impl<'lua, 'fut> LuaSchedulerExt<'lua, 'fut> for Lua
where
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
F: 'static + Fn(&'lua Lua, A) -> FR,
FR: 'static + Future<Output = LuaResult<R>>,
'lua: 'fut,
{
let coroutine_yield = self
.globals()
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<A, R, F, FR>(&'lua self, func: F) -> LuaResult<LuaFunction<'lua>>
where
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
F: 'static + Fn(&'lua Lua, A) -> FR,
FR: 'fut + Future<Output = LuaResult<R>>,
{
let async_env = self.create_table_with_capacity(0, 2)?;
async_env.set(
"yield",
self.globals()
.get::<_, LuaTable>("coroutine")?
.get::<_, LuaFunction>("yield")?;
let schedule = LuaFunction::wrap(move |lua: &Lua, args: A| {
let thread = lua.current_thread().into_owned();
let future = func(lua, args);
lua.scheduler().schedule_future_thread(thread, future);
.get::<_, LuaFunction>("yield")?,
)?;
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
.load(chunk!({
$schedule(...)
return $coroutine_yield()
}))
.load(ASYNC_IMPL_LUA)
.set_name("async")
.into_function()?;
Ok(async_func)