Use static lua for now, to make lifetimes work

This commit is contained in:
Filip Tibell 2023-08-18 13:43:18 -05:00
parent e1fa89cf60
commit 1f72033a6a
5 changed files with 65 additions and 49 deletions

View file

@ -6,9 +6,11 @@ mod scheduler;
use self::scheduler::Scheduler; use self::scheduler::Scheduler;
pub use error::LuneError; pub use error::LuneError;
use mlua::Lua;
#[derive(Clone, Debug, Default)] #[derive(Debug, Clone)]
pub struct Lune { pub struct Lune {
lua: &'static Lua,
args: Vec<String>, args: Vec<String>,
} }
@ -16,8 +18,12 @@ impl Lune {
/** /**
Creates a new Lune script runner. Creates a new Lune script runner.
*/ */
#[allow(clippy::new_without_default)]
pub fn new() -> Self { pub fn new() -> Self {
Self::default() Self {
lua: Lua::new().into_static(),
args: Vec::new(),
}
} }
/** /**
@ -39,15 +45,25 @@ impl Lune {
script_name: impl AsRef<str>, script_name: impl AsRef<str>,
script_contents: impl AsRef<[u8]>, script_contents: impl AsRef<[u8]>,
) -> Result<ExitCode, LuneError> { ) -> Result<ExitCode, LuneError> {
let scheduler = Scheduler::new(); let scheduler = Scheduler::new(self.lua);
self.lua.set_app_data(scheduler.clone());
let main = scheduler let main = self
.lua .lua
.load(script_contents.as_ref()) .load(script_contents.as_ref())
.set_name(script_name.as_ref()); .set_name(script_name.as_ref());
scheduler.push_back(main, ())?; scheduler.push_back(main, ())?;
Ok(scheduler.run_to_completion().await) Ok(scheduler.run_to_completion().await)
} }
} }
impl Drop for Lune {
fn drop(&mut self) {
// SAFETY: The scheduler needs the static lifetime reference to lua,
// when dropped nothing outside of here has access to the scheduler
unsafe {
Lua::from_static(self.lua);
}
}
}

View file

@ -33,11 +33,11 @@ where
FR: IntoLuaMulti<'fut>, FR: IntoLuaMulti<'fut>,
F: Future<Output = LuaResult<FR>> + 'fut, F: Future<Output = LuaResult<FR>> + 'fut,
{ {
let thread = thread.into_owned_lua_thread(&self.lua)?; let thread = thread.into_owned_lua_thread(self.lua)?;
self.schedule_future(async move { self.schedule_future(async move {
let rets = fut.await.expect("Failed to receive result"); let rets = fut.await.expect("Failed to receive result");
let rets = rets let rets = rets
.into_lua_multi(&self.lua) .into_lua_multi(self.lua)
.expect("Failed to create return multi value"); .expect("Failed to create return multi value");
self.push_back(thread, rets) self.push_back(thread, rets)
.expect("Failed to schedule future thread"); .expect("Failed to schedule future thread");

View file

@ -39,7 +39,7 @@ where
{ {
Some(thread) => { Some(thread) => {
let thread_id = &thread.id(); let thread_id = &thread.id();
let (thread, args) = thread.into_inner(&self.lua); let (thread, args) = thread.into_inner(self.lua);
let sender = self let sender = self
.thread_senders .thread_senders
.borrow_mut() .borrow_mut()
@ -60,10 +60,10 @@ where
thread: impl IntoLuaOwnedThread, thread: impl IntoLuaOwnedThread,
args: impl IntoLuaMulti<'a>, args: impl IntoLuaMulti<'a>,
) -> LuaResult<SchedulerThreadId> { ) -> LuaResult<SchedulerThreadId> {
let thread = thread.into_owned_lua_thread(&self.lua)?; let thread = thread.into_owned_lua_thread(self.lua)?;
let args = args.into_lua_multi(&self.lua)?; let args = args.into_lua_multi(self.lua)?;
let thread = SchedulerThread::new(&self.lua, thread, args)?; let thread = SchedulerThread::new(self.lua, thread, args)?;
let thread_id = thread.id(); let thread_id = thread.id();
self.threads self.threads
@ -87,10 +87,10 @@ where
thread: impl IntoLuaOwnedThread, thread: impl IntoLuaOwnedThread,
args: impl IntoLuaMulti<'a>, args: impl IntoLuaMulti<'a>,
) -> LuaResult<SchedulerThreadId> { ) -> LuaResult<SchedulerThreadId> {
let thread = thread.into_owned_lua_thread(&self.lua)?; let thread = thread.into_owned_lua_thread(self.lua)?;
let args = args.into_lua_multi(&self.lua)?; let args = args.into_lua_multi(self.lua)?;
let thread = SchedulerThread::new(&self.lua, thread, args)?; let thread = SchedulerThread::new(self.lua, thread, args)?;
let thread_id = thread.id(); let thread_id = thread.id();
self.threads self.threads

View file

@ -2,6 +2,7 @@ use std::{
cell::RefCell, cell::RefCell,
collections::{HashMap, VecDeque}, collections::{HashMap, VecDeque},
pin::Pin, pin::Pin,
sync::Arc,
}; };
use futures_util::{stream::FuturesUnordered, Future}; use futures_util::{stream::FuturesUnordered, Future};
@ -23,30 +24,31 @@ use self::{
thread::{SchedulerThread, SchedulerThreadId, SchedulerThreadSender}, thread::{SchedulerThread, SchedulerThreadId, SchedulerThreadSender},
}; };
/** type SchedulerFuture<'fut> = Pin<Box<dyn Future<Output = ()> + 'fut>>;
Scheduler for Lua threads.
This wraps a [`Lua`] struct and exposes it as the `lua` property. /**
Scheduler for Lua threads and futures.
This scheduler can be cheaply cloned and the underlying state
and data will remain unchanged and accessible from all clones.
*/ */
#[derive(Debug)] #[derive(Debug, Clone)]
pub(crate) struct Scheduler<'fut> { pub(crate) struct Scheduler<'fut> {
pub(crate) lua: Lua, lua: &'static Lua,
state: SchedulerState, state: Arc<SchedulerState>,
threads: RefCell<VecDeque<SchedulerThread>>, threads: Arc<RefCell<VecDeque<SchedulerThread>>>,
thread_senders: RefCell<HashMap<SchedulerThreadId, SchedulerThreadSender>>, thread_senders: Arc<RefCell<HashMap<SchedulerThreadId, SchedulerThreadSender>>>,
futures: AsyncMutex<FuturesUnordered<Pin<Box<dyn Future<Output = ()> + 'fut>>>>, futures: Arc<AsyncMutex<FuturesUnordered<SchedulerFuture<'fut>>>>,
} }
impl<'fut> Scheduler<'fut> { impl<'fut> Scheduler<'fut> {
pub fn new() -> Self { pub fn new(lua: &'static Lua) -> Self {
let lua = Lua::new();
Self { Self {
lua, lua,
state: SchedulerState::new(), state: Arc::new(SchedulerState::new()),
threads: RefCell::new(VecDeque::new()), threads: Arc::new(RefCell::new(VecDeque::new())),
thread_senders: RefCell::new(HashMap::new()), thread_senders: Arc::new(RefCell::new(HashMap::new())),
futures: AsyncMutex::new(FuturesUnordered::new()), futures: Arc::new(AsyncMutex::new(FuturesUnordered::new())),
} }
} }
} }

View file

@ -13,32 +13,29 @@ return yield()
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<'lua, 'fut> pub trait LuaSchedulerExt {
where
'lua: 'fut,
{
/** /**
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<A, R, F, FR>(&'lua self, func: F) -> LuaResult<LuaFunction<'lua>> fn create_async_function<A, R, F, FR>(
&'static self,
func: F,
) -> LuaResult<LuaFunction<'static>>
where where
A: FromLuaMulti<'lua>, A: FromLuaMulti<'static>,
R: IntoLuaMulti<'lua>, R: IntoLuaMulti<'static>,
F: Fn(&'lua Lua, A) -> FR + 'static, F: Fn(&'static Lua, A) -> FR + 'static,
FR: Future<Output = LuaResult<R>> + 'fut; FR: Future<Output = LuaResult<R>> + 'static;
} }
impl<'lua, 'fut> LuaSchedulerExt<'lua, 'fut> for Lua impl LuaSchedulerExt for Lua {
fn create_async_function<A, R, F, FR>(&'static self, func: F) -> LuaResult<LuaFunction<'static>>
where where
'lua: 'fut, A: FromLuaMulti<'static>,
{ R: IntoLuaMulti<'static>,
fn create_async_function<A, R, F, FR>(&'lua self, func: F) -> LuaResult<LuaFunction<'lua>> F: Fn(&'static Lua, A) -> FR + 'static,
where FR: Future<Output = LuaResult<R>> + 'static,
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>,
F: Fn(&'lua Lua, A) -> FR + 'static,
FR: Future<Output = LuaResult<R>> + 'fut,
{ {
let async_env = self.create_table_with_capacity(0, 2)?; let async_env = self.create_table_with_capacity(0, 2)?;
@ -58,7 +55,8 @@ where
.app_data_ref::<&Scheduler>() .app_data_ref::<&Scheduler>()
.expect("Lua struct is missing scheduler"); .expect("Lua struct is missing scheduler");
// FIXME: `self` escapes outside of method because we are borrowing `func`? // FIXME: `self` escapes outside of method because we are borrowing `func`?
// sched.schedule_future_thread(thread, future)?; // For now we solve this by just using a &'static Lua reference everywhere
sched.schedule_future_thread(thread, future)?;
Ok(()) Ok(())
}), }),
)?; )?;