From ab386e000dfbb533e0920b12c941ea2ec7e06b48 Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Thu, 17 Aug 2023 21:24:20 -0500 Subject: [PATCH] Add lifetimes because they are fun --- src/lune/scheduler/impl_async.rs | 10 ++-- src/lune/scheduler/impl_runner.rs | 31 ++++++++++-- src/lune/scheduler/impl_threads.rs | 5 +- src/lune/scheduler/mod.rs | 21 ++++---- src/lune/scheduler/traits.rs | 79 ++++++++++++++++++------------ 5 files changed, 95 insertions(+), 51 deletions(-) diff --git a/src/lune/scheduler/impl_async.rs b/src/lune/scheduler/impl_async.rs index b87c645..b43f0e7 100644 --- a/src/lune/scheduler/impl_async.rs +++ b/src/lune/scheduler/impl_async.rs @@ -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(&self, fut: F) where - F: 'static + Future, + F: 'fut + Future, { 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(&'lua self, thread: T, fut: F) -> LuaResult<()> + pub fn schedule_future_thread(&'fut self, thread: T, fut: F) -> LuaResult<()> where - T: 'static + IntoLuaOwnedThread, - F: 'static + Future>, + T: IntoLuaOwnedThread, R: IntoLuaMulti<'fut>, + F: 'fut + Future>, { let thread = thread.into_owned_lua_thread(&self.lua)?; diff --git a/src/lune/scheduler/impl_runner.rs b/src/lune/scheduler/impl_runner.rs index 0ce52b8..ff37ca0 100644 --- a/src/lune/scheduler/impl_runner.rs +++ b/src/lune/scheduler/impl_runner.rs @@ -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 + } } diff --git a/src/lune/scheduler/impl_threads.rs b/src/lune/scheduler/impl_threads.rs index ca3de93..da043c0 100644 --- a/src/lune/scheduler/impl_threads.rs +++ b/src/lune/scheduler/impl_threads.rs @@ -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. */ diff --git a/src/lune/scheduler/mod.rs b/src/lune/scheduler/mod.rs index 3a2a070..a6937c3 100644 --- a/src/lune/scheduler/mod.rs +++ b/src/lune/scheduler/mod.rs @@ -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, - inner: Arc, +pub(crate) struct Scheduler<'fut> { + inner: Arc>, } -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, state: SchedulerState, threads: RefCell>, thread_senders: RefCell>, - futures: AsyncMutex>>>>, + futures: AsyncMutex + 'fut>>>>, } -impl SchedulerImpl { +impl<'fut> SchedulerImpl<'fut> { fn new(lua: Arc) -> Self { Self { lua, diff --git a/src/lune/scheduler/traits.rs b/src/lune/scheduler/traits.rs index 0b26b7c..bd24567 100644 --- a/src/lune/scheduler/traits.rs +++ b/src/lune/scheduler/traits.rs @@ -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; /** 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> + fn create_async_function(&'lua self, func: F) -> LuaResult> where A: FromLuaMulti<'lua>, R: IntoLuaMulti<'lua>, F: 'static + Fn(&'lua Lua, A) -> FR, - FR: 'static + Future>; + FR: 'fut + Future>; } -impl LuaSchedulerExt for Lua { - fn scheduler(&self) -> &Scheduler { - *self - .app_data_ref::<&Scheduler>() - .expect("Lua struct is missing scheduler") +impl<'lua, 'fut> LuaSchedulerExt<'lua, 'fut> for Lua +where + 'lua: 'fut, +{ + fn new_with_scheduler() -> Arc { + 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> + fn create_async_function(&'lua self, func: F) -> LuaResult> where A: FromLuaMulti<'lua>, R: IntoLuaMulti<'lua>, F: 'static + Fn(&'lua Lua, A) -> FR, - FR: 'static + Future>, + FR: 'fut + Future>, { - let coroutine_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); - Ok(()) - }); + let async_env = self.create_table_with_capacity(0, 2)?; + + async_env.set( + "yield", + self.globals() + .get::<_, LuaTable>("coroutine")? + .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)