mirror of
https://github.com/lune-org/lune.git
synced 2024-12-13 05:20:37 +00:00
Scheduler now manages entire Lua struct, elide lifetimes where possible
This commit is contained in:
parent
1b7287a742
commit
dc80b1c28f
6 changed files with 81 additions and 114 deletions
|
@ -1,6 +1,4 @@
|
|||
use std::{process::ExitCode, sync::Arc};
|
||||
|
||||
use mlua::prelude::*;
|
||||
use std::process::ExitCode;
|
||||
|
||||
mod error;
|
||||
mod scheduler;
|
||||
|
@ -41,18 +39,8 @@ impl Lune {
|
|||
script_name: impl AsRef<str>,
|
||||
script_contents: impl AsRef<[u8]>,
|
||||
) -> Result<ExitCode, LuneError> {
|
||||
let lua = Arc::new(Lua::new());
|
||||
let sched = Scheduler::new(Arc::clone(&lua));
|
||||
|
||||
let main_fn = lua
|
||||
.load(script_contents.as_ref())
|
||||
.set_name(script_name.as_ref())
|
||||
.into_function()?;
|
||||
let main_thread = lua.create_thread(main_fn)?.into_owned();
|
||||
|
||||
sched
|
||||
.push_back(main_thread, ())
|
||||
.expect("Failed to enqueue thread for main");
|
||||
Ok(sched.run_to_completion().await)
|
||||
Ok(Scheduler::new()
|
||||
.run_main(script_name, script_contents)
|
||||
.await)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
use futures_util::Future;
|
||||
use mlua::prelude::*;
|
||||
|
||||
use super::SchedulerImpl;
|
||||
use super::Scheduler;
|
||||
|
||||
impl<'lua, 'fut> SchedulerImpl<'fut>
|
||||
impl<'lua, 'fut> Scheduler<'fut>
|
||||
where
|
||||
'lua: 'fut,
|
||||
{
|
||||
/**
|
||||
Schedules a plain future to run whenever the scheduler is available.
|
||||
*/
|
||||
pub fn schedule_future<F>(&'lua self, fut: F)
|
||||
pub fn schedule_future<F>(&'fut self, fut: F)
|
||||
where
|
||||
F: 'fut + Future<Output = ()>,
|
||||
{
|
||||
|
@ -24,10 +24,9 @@ where
|
|||
/**
|
||||
Schedules the given `thread` to run when the given `fut` completes.
|
||||
*/
|
||||
pub fn schedule_future_thread<R, F>(&'lua self, thread: LuaOwnedThread, fut: F) -> LuaResult<()>
|
||||
pub fn schedule_future_thread<F>(&'fut self, thread: LuaOwnedThread, fut: F) -> LuaResult<()>
|
||||
where
|
||||
R: IntoLuaMulti<'fut>,
|
||||
F: 'fut + Future<Output = LuaResult<R>>,
|
||||
F: 'fut + Future<Output = LuaResult<LuaMultiValue<'fut>>>,
|
||||
{
|
||||
self.schedule_future(async move {
|
||||
let rets = fut.await.expect("Failed to receive result");
|
||||
|
|
|
@ -2,11 +2,14 @@ use std::{process::ExitCode, sync::Arc};
|
|||
|
||||
use futures_util::StreamExt;
|
||||
use mlua::prelude::*;
|
||||
|
||||
use tokio::task::LocalSet;
|
||||
|
||||
use super::SchedulerImpl;
|
||||
use super::{IntoLuaOwnedThread, Scheduler};
|
||||
|
||||
impl<'lua, 'fut> SchedulerImpl<'fut>
|
||||
const EMPTY_MULTI_VALUE: LuaMultiValue = LuaMultiValue::new();
|
||||
|
||||
impl<'lua, 'fut> Scheduler<'fut>
|
||||
where
|
||||
'lua: 'fut,
|
||||
{
|
||||
|
@ -15,7 +18,7 @@ where
|
|||
|
||||
Returns `true` if any thread was resumed, `false` otherwise.
|
||||
*/
|
||||
fn run_lua_threads(&'lua self) -> bool {
|
||||
fn run_lua_threads(&self) -> bool {
|
||||
if self.state.has_exit_code() {
|
||||
return false;
|
||||
}
|
||||
|
@ -60,7 +63,7 @@ where
|
|||
|
||||
Returns `true` if any future was resumed, `false` otherwise.
|
||||
*/
|
||||
async fn run_futures(&'lua self) -> bool {
|
||||
async fn run_futures(&self) -> bool {
|
||||
let mut resumed_any = false;
|
||||
|
||||
let mut futs = self
|
||||
|
@ -84,32 +87,31 @@ where
|
|||
|
||||
Will emit lua output and errors to stdout and stderr.
|
||||
*/
|
||||
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,
|
||||
// if any thread was resumed it may have spawned futures
|
||||
let resumed_lua = self.run_lua_threads();
|
||||
pub async fn run_to_completion(&self) -> ExitCode {
|
||||
let set = LocalSet::new();
|
||||
let _guard = set.enter();
|
||||
|
||||
// 2. If we got a manual exit code from lua we should
|
||||
// not try to wait for any pending futures to complete
|
||||
if self.state.has_exit_code() {
|
||||
break;
|
||||
}
|
||||
loop {
|
||||
// 1. Run lua threads until exit or there are none left,
|
||||
// if any thread was resumed it may have spawned futures
|
||||
let resumed_lua = self.run_lua_threads();
|
||||
|
||||
// 3. Keep resuming futures until we get a new lua thread to
|
||||
// resume, or until we don't have any futures left to wait for
|
||||
let resumed_fut = self.run_futures().await;
|
||||
|
||||
// 4. If we did not resume any lua threads, and we have no futures
|
||||
// remaining either, we have now run the scheduler until completion
|
||||
if !resumed_lua && !resumed_fut {
|
||||
break;
|
||||
}
|
||||
// 2. If we got a manual exit code from lua we should
|
||||
// not try to wait for any pending futures to complete
|
||||
if self.state.has_exit_code() {
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
LocalSet::new().run_until(fut).await;
|
||||
// 3. Keep resuming futures until we get a new lua thread to
|
||||
// resume, or until we don't have any futures left to wait for
|
||||
let resumed_fut = self.run_futures().await;
|
||||
|
||||
// 4. If we did not resume any lua threads, and we have no futures
|
||||
// remaining either, we have now run the scheduler until completion
|
||||
if !resumed_lua && !resumed_fut {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(code) = self.state.exit_code() {
|
||||
ExitCode::from(code)
|
||||
|
@ -119,4 +121,31 @@ where
|
|||
ExitCode::SUCCESS
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Runs a script with the given `script_name` and `script_contents` to completion.
|
||||
|
||||
Refer to [`run_to_completion`] for additional details.
|
||||
*/
|
||||
pub async fn run_main(
|
||||
self,
|
||||
script_name: impl AsRef<str>,
|
||||
script_contents: impl AsRef<[u8]>,
|
||||
) -> ExitCode {
|
||||
let main_fn = self
|
||||
.lua
|
||||
.load(script_contents.as_ref())
|
||||
.set_name(script_name.as_ref())
|
||||
.into_function()
|
||||
.expect("Failed to create function for main");
|
||||
|
||||
let main_thread = main_fn
|
||||
.into_owned_lua_thread(&self.lua)
|
||||
.expect("Failed to create thread for main");
|
||||
|
||||
self.push_back(main_thread, EMPTY_MULTI_VALUE)
|
||||
.expect("Failed to enqueue thread for main");
|
||||
|
||||
self.run_to_completion().await
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,10 +4,10 @@ use mlua::prelude::*;
|
|||
|
||||
use super::{
|
||||
thread::{SchedulerThread, SchedulerThreadId, SchedulerThreadSender},
|
||||
SchedulerImpl,
|
||||
Scheduler,
|
||||
};
|
||||
|
||||
impl<'lua, 'fut> SchedulerImpl<'fut>
|
||||
impl<'lua, 'fut> Scheduler<'fut>
|
||||
where
|
||||
'lua: 'fut,
|
||||
{
|
||||
|
@ -28,8 +28,8 @@ where
|
|||
Returns `None` if there are no threads left to run.
|
||||
*/
|
||||
pub(super) fn pop_thread(
|
||||
&'lua self,
|
||||
) -> LuaResult<Option<(LuaOwnedThread, LuaMultiValue<'lua>, SchedulerThreadSender)>> {
|
||||
&self,
|
||||
) -> LuaResult<Option<(LuaOwnedThread, LuaMultiValue<'_>, SchedulerThreadSender)>> {
|
||||
match self
|
||||
.threads
|
||||
.try_borrow_mut()
|
||||
|
@ -56,12 +56,10 @@ where
|
|||
right away, before any other currently scheduled threads.
|
||||
*/
|
||||
pub fn push_front(
|
||||
&'lua self,
|
||||
&self,
|
||||
thread: LuaOwnedThread,
|
||||
args: impl IntoLuaMulti<'lua>,
|
||||
args: LuaMultiValue<'_>,
|
||||
) -> LuaResult<SchedulerThreadId> {
|
||||
let args = args.into_lua_multi(&self.lua)?;
|
||||
|
||||
let thread = SchedulerThread::new(&self.lua, thread, args)?;
|
||||
let thread_id = thread.id();
|
||||
|
||||
|
@ -82,12 +80,10 @@ where
|
|||
after all other current threads have been resumed.
|
||||
*/
|
||||
pub fn push_back(
|
||||
&'lua self,
|
||||
&self,
|
||||
thread: LuaOwnedThread,
|
||||
args: impl IntoLuaMulti<'lua>,
|
||||
args: LuaMultiValue<'_>,
|
||||
) -> LuaResult<SchedulerThreadId> {
|
||||
let args = args.into_lua_multi(&self.lua)?;
|
||||
|
||||
let thread = SchedulerThread::new(&self.lua, thread, args)?;
|
||||
let thread_id = thread.id();
|
||||
|
||||
|
@ -107,9 +103,9 @@ where
|
|||
Waits for the given thread to finish running, and returns its result.
|
||||
*/
|
||||
pub async fn wait_for_thread(
|
||||
&'lua self,
|
||||
&self,
|
||||
thread_id: SchedulerThreadId,
|
||||
) -> LuaResult<LuaMultiValue<'lua>> {
|
||||
) -> LuaResult<LuaMultiValue<'_>> {
|
||||
let mut recv = {
|
||||
let senders = self.thread_senders.borrow();
|
||||
let sender = senders
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
use std::{
|
||||
cell::RefCell,
|
||||
collections::{HashMap, VecDeque},
|
||||
ops::Deref,
|
||||
pin::Pin,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use futures_util::{stream::FuturesUnordered, Future};
|
||||
|
@ -28,51 +26,21 @@ use self::{
|
|||
/**
|
||||
Scheduler for Lua threads.
|
||||
|
||||
Can be cheaply cloned, and any clone will refer
|
||||
to the same underlying scheduler and Lua struct.
|
||||
*/
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct Scheduler<'fut> {
|
||||
inner: Arc<SchedulerImpl<'fut>>,
|
||||
}
|
||||
|
||||
impl<'fut> Scheduler<'fut> {
|
||||
/**
|
||||
Creates a new scheduler for the given [`Lua`] struct.
|
||||
*/
|
||||
pub fn new(lua: Arc<Lua>) -> Self {
|
||||
let sched_lua = Arc::clone(&lua);
|
||||
let sched_impl = SchedulerImpl::new(sched_lua);
|
||||
|
||||
let inner = Arc::new(sched_impl);
|
||||
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'fut> Deref for Scheduler<'fut> {
|
||||
type Target = SchedulerImpl<'fut>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Implementation of scheduler for Lua threads.
|
||||
|
||||
Not meant to be used directly, use [`Scheduler`] instead.
|
||||
This wraps a [`Lua`] struct and exposes it as the `lua` property.
|
||||
*/
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct SchedulerImpl<'fut> {
|
||||
lua: Arc<Lua>,
|
||||
pub(crate) struct Scheduler<'fut> {
|
||||
pub(crate) lua: Lua,
|
||||
state: SchedulerState,
|
||||
threads: RefCell<VecDeque<SchedulerThread>>,
|
||||
thread_senders: RefCell<HashMap<SchedulerThreadId, SchedulerThreadSender>>,
|
||||
futures: AsyncMutex<FuturesUnordered<Pin<Box<dyn Future<Output = ()> + 'fut>>>>,
|
||||
}
|
||||
|
||||
impl<'fut> SchedulerImpl<'fut> {
|
||||
fn new(lua: Arc<Lua>) -> Self {
|
||||
impl<'fut> Scheduler<'fut> {
|
||||
pub fn new() -> Self {
|
||||
let lua = Lua::new();
|
||||
|
||||
Self {
|
||||
lua,
|
||||
state: SchedulerState::new(),
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use futures_util::Future;
|
||||
use mlua::prelude::*;
|
||||
|
||||
|
@ -19,11 +17,6 @@ pub trait LuaSchedulerExt<'lua, 'fut>
|
|||
where
|
||||
'lua: 'fut,
|
||||
{
|
||||
/**
|
||||
Creates a new [`Lua`] struct with a [`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.
|
||||
|
@ -40,12 +33,6 @@ impl<'lua, 'fut> LuaSchedulerExt<'lua, 'fut> for Lua
|
|||
where
|
||||
'lua: 'fut,
|
||||
{
|
||||
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>,
|
||||
|
|
Loading…
Reference in a new issue