#![allow(unused_imports)]
#![allow(clippy::missing_errors_doc)]
use std::{
cell::Cell, future::Future, process::ExitCode, rc::Weak as WeakRc, sync::Weak as WeakArc,
};
use async_executor::{Executor, Task};
use mlua::prelude::*;
use tracing::trace;
use crate::{
exit::Exit,
queue::{DeferredThreadQueue, FuturesQueue, SpawnedThreadQueue},
scheduler::Scheduler,
threads::{ThreadId, ThreadMap},
};
pub trait IntoLuaThread {
fn into_lua_thread(self, lua: &Lua) -> LuaResult<LuaThread>;
}
impl IntoLuaThread for LuaThread {
fn into_lua_thread(self, _: &Lua) -> LuaResult<LuaThread> {
Ok(self)
}
}
impl IntoLuaThread for LuaFunction {
fn into_lua_thread(self, lua: &Lua) -> LuaResult<LuaThread> {
lua.create_thread(self)
}
}
impl IntoLuaThread for LuaChunk<'_> {
fn into_lua_thread(self, lua: &Lua) -> LuaResult<LuaThread> {
lua.create_thread(self.into_function()?)
}
}
impl<T> IntoLuaThread for &T
where
T: IntoLuaThread + Clone,
{
fn into_lua_thread(self, lua: &Lua) -> LuaResult<LuaThread> {
self.clone().into_lua_thread(lua)
}
}
pub trait LuaSchedulerExt {
fn set_exit_code(&self, code: u8);
fn push_thread_front(
&self,
thread: impl IntoLuaThread,
args: impl IntoLuaMulti,
) -> LuaResult<ThreadId>;
fn push_thread_back(
&self,
thread: impl IntoLuaThread,
args: impl IntoLuaMulti,
) -> LuaResult<ThreadId>;
fn track_thread(&self, id: ThreadId);
fn get_thread_result(&self, id: ThreadId) -> Option<LuaResult<LuaMultiValue>>;
fn wait_for_thread(&self, id: ThreadId) -> impl Future<Output = ()>;
}
pub trait LuaSpawnExt {
fn spawn<F, T>(&self, fut: F) -> Task<T>
where
F: Future<Output = T> + Send + 'static,
T: Send + 'static;
fn spawn_local<F>(&self, fut: F)
where
F: Future<Output = ()> + 'static;
fn spawn_blocking<F, T>(&self, f: F) -> Task<T>
where
F: FnOnce() -> T + Send + 'static,
T: Send + 'static;
}
impl LuaSchedulerExt for Lua {
fn set_exit_code(&self, code: u8) {
let exit = self
.app_data_ref::<Exit>()
.expect("exit code can only be set from within an active scheduler");
exit.set(code);
}
fn push_thread_front(
&self,
thread: impl IntoLuaThread,
args: impl IntoLuaMulti,
) -> LuaResult<ThreadId> {
let queue = self
.app_data_ref::<SpawnedThreadQueue>()
.expect("lua threads can only be pushed from within an active scheduler");
queue.push_item(self, thread, args)
}
fn push_thread_back(
&self,
thread: impl IntoLuaThread,
args: impl IntoLuaMulti,
) -> LuaResult<ThreadId> {
let queue = self
.app_data_ref::<DeferredThreadQueue>()
.expect("lua threads can only be pushed from within an active scheduler");
queue.push_item(self, thread, args)
}
fn track_thread(&self, id: ThreadId) {
let map = self
.app_data_ref::<ThreadMap>()
.expect("lua threads can only be tracked from within an active scheduler");
map.track(id);
}
fn get_thread_result(&self, id: ThreadId) -> Option<LuaResult<LuaMultiValue>> {
let map = self
.app_data_ref::<ThreadMap>()
.expect("lua threads results can only be retrieved from within an active scheduler");
map.remove(id)
}
fn wait_for_thread(&self, id: ThreadId) -> impl Future<Output = ()> {
let map = self
.app_data_ref::<ThreadMap>()
.expect("lua threads results can only be retrieved from within an active scheduler");
async move { map.listen(id).await }
}
}
impl LuaSpawnExt for Lua {
fn spawn<F, T>(&self, fut: F) -> Task<T>
where
F: Future<Output = T> + Send + 'static,
T: Send + 'static,
{
let exec = self
.app_data_ref::<WeakArc<Executor>>()
.expect("tasks can only be spawned within an active scheduler")
.upgrade()
.expect("executor was dropped");
trace!("spawning future on executor");
exec.spawn(fut)
}
fn spawn_local<F>(&self, fut: F)
where
F: Future<Output = ()> + 'static,
{
let queue = self
.app_data_ref::<FuturesQueue>()
.expect("tasks can only be spawned within an active scheduler");
trace!("spawning local task on executor");
queue.push_item(fut);
}
fn spawn_blocking<F, T>(&self, f: F) -> Task<T>
where
F: FnOnce() -> T + Send + 'static,
T: Send + 'static,
{
let exec = self
.app_data_ref::<WeakArc<Executor>>()
.expect("tasks can only be spawned within an active scheduler")
.upgrade()
.expect("executor was dropped");
trace!("spawning blocking task on executor");
exec.spawn(blocking::unblock(f))
}
}