mirror of
https://github.com/lune-org/lune.git
synced 2025-03-04 11:11:39 +00:00
Implement basic abs path require, propagate async errors back to lua threads
This commit is contained in:
parent
bcef44e286
commit
dcb989fd92
9 changed files with 95 additions and 47 deletions
|
@ -2,12 +2,19 @@ use mlua::prelude::*;
|
||||||
|
|
||||||
use super::context::*;
|
use super::context::*;
|
||||||
|
|
||||||
pub(super) async fn require<'lua>(
|
pub(super) async fn require<'lua, 'ctx>(
|
||||||
_lua: &'lua Lua,
|
lua: &'lua Lua,
|
||||||
_ctx: RequireContext,
|
ctx: &'ctx RequireContext,
|
||||||
path: &str,
|
path: &str,
|
||||||
) -> LuaResult<LuaValue<'lua>> {
|
) -> LuaResult<LuaMultiValue<'lua>>
|
||||||
Err(LuaError::runtime(format!(
|
where
|
||||||
"TODO: Support require for absolute paths (tried to require '{path}')"
|
'lua: 'ctx,
|
||||||
)))
|
{
|
||||||
|
if ctx.is_cached(path)? {
|
||||||
|
ctx.get_from_cache(lua, path)
|
||||||
|
} else if ctx.is_pending(path)? {
|
||||||
|
ctx.wait_for_cache(lua, path).await
|
||||||
|
} else {
|
||||||
|
ctx.load(lua, path).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,12 +2,15 @@ use mlua::prelude::*;
|
||||||
|
|
||||||
use super::context::*;
|
use super::context::*;
|
||||||
|
|
||||||
pub(super) async fn require<'lua>(
|
pub(super) async fn require<'lua, 'ctx>(
|
||||||
_lua: &'lua Lua,
|
_lua: &'lua Lua,
|
||||||
_ctx: RequireContext,
|
_ctx: &'ctx RequireContext,
|
||||||
alias: &str,
|
alias: &str,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> LuaResult<LuaValue<'lua>> {
|
) -> LuaResult<LuaMultiValue<'lua>>
|
||||||
|
where
|
||||||
|
'lua: 'ctx,
|
||||||
|
{
|
||||||
Err(LuaError::runtime(format!(
|
Err(LuaError::runtime(format!(
|
||||||
"TODO: Support require for built-in libraries (tried to require '{name}' with alias '{alias}')"
|
"TODO: Support require for built-in libraries (tried to require '{name}' with alias '{alias}')"
|
||||||
)))
|
)))
|
||||||
|
|
|
@ -2,11 +2,14 @@ use mlua::prelude::*;
|
||||||
|
|
||||||
use super::context::*;
|
use super::context::*;
|
||||||
|
|
||||||
pub(super) async fn require<'lua>(
|
pub(super) async fn require<'lua, 'ctx>(
|
||||||
_lua: &'lua Lua,
|
_lua: &'lua Lua,
|
||||||
_ctx: RequireContext,
|
_ctx: &'ctx RequireContext,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> LuaResult<LuaValue<'lua>> {
|
) -> LuaResult<LuaMultiValue<'lua>>
|
||||||
|
where
|
||||||
|
'lua: 'ctx,
|
||||||
|
{
|
||||||
Err(LuaError::runtime(format!(
|
Err(LuaError::runtime(format!(
|
||||||
"TODO: Support require for built-in libraries (tried to require '{name}')"
|
"TODO: Support require for built-in libraries (tried to require '{name}')"
|
||||||
)))
|
)))
|
||||||
|
|
|
@ -23,17 +23,15 @@ impl RequireContext {
|
||||||
context should be created per [`Lua`] struct, creating more
|
context should be created per [`Lua`] struct, creating more
|
||||||
than one context may lead to undefined require-behavior.
|
than one context may lead to undefined require-behavior.
|
||||||
*/
|
*/
|
||||||
pub fn create(lua: &Lua) {
|
pub fn new() -> Self {
|
||||||
let this = Self {
|
Self {
|
||||||
// TODO: Set to false by default, load some kind of config
|
// TODO: Set to false by default, load some kind of config
|
||||||
// or env var to check if we should be using absolute paths
|
// or env var to check if we should be using absolute paths
|
||||||
use_absolute_paths: true,
|
use_absolute_paths: true,
|
||||||
working_directory: env::current_dir().expect("Failed to get current working directory"),
|
working_directory: env::current_dir().expect("Failed to get current working directory"),
|
||||||
cache_results: Arc::new(AsyncMutex::new(HashMap::new())),
|
cache_results: Arc::new(AsyncMutex::new(HashMap::new())),
|
||||||
cache_pending: Arc::new(AsyncMutex::new(HashMap::new())),
|
cache_pending: Arc::new(AsyncMutex::new(HashMap::new())),
|
||||||
};
|
}
|
||||||
lua.set_named_registry_value(REGISTRY_KEY, this)
|
|
||||||
.expect("Failed to insert RequireContext into registry");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -102,9 +100,9 @@ impl RequireContext {
|
||||||
path will first be transformed into an absolute path.
|
path will first be transformed into an absolute path.
|
||||||
*/
|
*/
|
||||||
pub fn get_from_cache<'lua>(
|
pub fn get_from_cache<'lua>(
|
||||||
&'lua self,
|
&self,
|
||||||
lua: &'lua Lua,
|
lua: &'lua Lua,
|
||||||
path: impl AsRef<str> + 'lua,
|
path: impl AsRef<str>,
|
||||||
) -> LuaResult<LuaMultiValue<'lua>> {
|
) -> LuaResult<LuaMultiValue<'lua>> {
|
||||||
let path = self.abs_path(path);
|
let path = self.abs_path(path);
|
||||||
|
|
||||||
|
@ -136,9 +134,9 @@ impl RequireContext {
|
||||||
path will first be transformed into an absolute path.
|
path will first be transformed into an absolute path.
|
||||||
*/
|
*/
|
||||||
pub async fn wait_for_cache<'lua>(
|
pub async fn wait_for_cache<'lua>(
|
||||||
&'lua self,
|
&self,
|
||||||
lua: &'lua Lua,
|
lua: &'lua Lua,
|
||||||
path: impl AsRef<str> + 'lua,
|
path: impl AsRef<str>,
|
||||||
) -> LuaResult<LuaMultiValue<'lua>> {
|
) -> LuaResult<LuaMultiValue<'lua>> {
|
||||||
let path = self.abs_path(path);
|
let path = self.abs_path(path);
|
||||||
let sched = lua
|
let sched = lua
|
||||||
|
@ -166,9 +164,9 @@ impl RequireContext {
|
||||||
path will first be transformed into an absolute path.
|
path will first be transformed into an absolute path.
|
||||||
*/
|
*/
|
||||||
pub async fn load<'lua>(
|
pub async fn load<'lua>(
|
||||||
&'lua self,
|
&self,
|
||||||
lua: &'lua Lua,
|
lua: &'lua Lua,
|
||||||
path: impl AsRef<str> + 'lua,
|
path: impl AsRef<str>,
|
||||||
) -> LuaResult<LuaMultiValue<'lua>> {
|
) -> LuaResult<LuaMultiValue<'lua>> {
|
||||||
let path = self.abs_path(path);
|
let path = self.abs_path(path);
|
||||||
let sched = lua
|
let sched = lua
|
||||||
|
|
|
@ -11,31 +11,34 @@ mod builtin;
|
||||||
mod relative;
|
mod relative;
|
||||||
|
|
||||||
pub fn create(lua: &'static Lua) -> LuaResult<impl IntoLua<'_>> {
|
pub fn create(lua: &'static Lua) -> LuaResult<impl IntoLua<'_>> {
|
||||||
RequireContext::create(lua);
|
lua.set_app_data(RequireContext::new());
|
||||||
|
|
||||||
lua.create_async_function(|lua, path: LuaString| async move {
|
lua.create_async_function(|lua, path: LuaString| async move {
|
||||||
let context = RequireContext::from(lua);
|
|
||||||
|
|
||||||
let path = path
|
let path = path
|
||||||
.to_str()
|
.to_str()
|
||||||
.into_lua_err()
|
.into_lua_err()
|
||||||
.context("Failed to parse require path as string")?
|
.context("Failed to parse require path as string")?
|
||||||
.to_string();
|
.to_string();
|
||||||
|
|
||||||
if let Some(builtin_name) = path
|
let context = lua
|
||||||
|
.app_data_ref()
|
||||||
|
.expect("Failed to get RequireContext from app data");
|
||||||
|
|
||||||
|
let res = if let Some(builtin_name) = path
|
||||||
.strip_prefix("@lune/")
|
.strip_prefix("@lune/")
|
||||||
.map(|name| name.to_ascii_lowercase())
|
.map(|name| name.to_ascii_lowercase())
|
||||||
{
|
{
|
||||||
builtin::require(lua, context, &builtin_name).await
|
builtin::require(lua, &context, &builtin_name).await
|
||||||
} else if let Some(aliased_path) = path.strip_prefix('@') {
|
} else if let Some(aliased_path) = path.strip_prefix('@') {
|
||||||
let (alias, name) = aliased_path.split_once('/').ok_or(LuaError::runtime(
|
let (alias, name) = aliased_path.split_once('/').ok_or(LuaError::runtime(
|
||||||
"Require with custom alias must contain '/' delimiter",
|
"Require with custom alias must contain '/' delimiter",
|
||||||
))?;
|
))?;
|
||||||
alias::require(lua, context, alias, name).await
|
alias::require(lua, &context, alias, name).await
|
||||||
} else if context.use_absolute_paths() {
|
} else if context.use_absolute_paths() {
|
||||||
absolute::require(lua, context, &path).await
|
absolute::require(lua, &context, &path).await
|
||||||
} else {
|
} else {
|
||||||
relative::require(lua, context, &path).await
|
relative::require(lua, &context, &path).await
|
||||||
}
|
};
|
||||||
|
|
||||||
|
res.clone()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,14 @@ use mlua::prelude::*;
|
||||||
|
|
||||||
use super::context::*;
|
use super::context::*;
|
||||||
|
|
||||||
pub(super) async fn require<'lua>(
|
pub(super) async fn require<'lua, 'ctx>(
|
||||||
_lua: &'lua Lua,
|
_lua: &'lua Lua,
|
||||||
_ctx: RequireContext,
|
_ctx: &'ctx RequireContext,
|
||||||
path: &str,
|
path: &str,
|
||||||
) -> LuaResult<LuaValue<'lua>> {
|
) -> LuaResult<LuaMultiValue<'lua>>
|
||||||
|
where
|
||||||
|
'lua: 'ctx,
|
||||||
|
{
|
||||||
Err(LuaError::runtime(format!(
|
Err(LuaError::runtime(format!(
|
||||||
"TODO: Support require for absolute paths (tried to require '{path}')"
|
"TODO: Support require for absolute paths (tried to require '{path}')"
|
||||||
)))
|
)))
|
||||||
|
|
|
@ -37,13 +37,20 @@ where
|
||||||
{
|
{
|
||||||
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 {
|
||||||
// TODO: Throw any error back to lua instead of panicking here
|
match fut.await.and_then(|rets| rets.into_lua_multi(self.lua)) {
|
||||||
let rets = fut.await.expect("Failed to receive result");
|
Err(e) => {
|
||||||
let rets = rets
|
self.state.set_lua_error(e);
|
||||||
.into_lua_multi(self.lua)
|
// NOTE: We push the thread to the front of the scheduler
|
||||||
.expect("Failed to create return multi value");
|
// to ensure that it runs first to be able to catch the
|
||||||
self.push_back(thread, rets)
|
// stored error from within the scheduler lua interrupt
|
||||||
|
self.push_front(thread, ())
|
||||||
.expect("Failed to schedule future thread");
|
.expect("Failed to schedule future thread");
|
||||||
|
}
|
||||||
|
Ok(v) => {
|
||||||
|
self.push_back(thread, v)
|
||||||
|
.expect("Failed to schedule future thread");
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -44,13 +44,23 @@ pub(crate) struct Scheduler<'lua, 'fut> {
|
||||||
|
|
||||||
impl<'lua, 'fut> Scheduler<'lua, 'fut> {
|
impl<'lua, 'fut> Scheduler<'lua, 'fut> {
|
||||||
pub fn new(lua: &'lua Lua) -> Self {
|
pub fn new(lua: &'lua Lua) -> Self {
|
||||||
Self {
|
let this = Self {
|
||||||
lua,
|
lua,
|
||||||
state: Arc::new(SchedulerState::new()),
|
state: Arc::new(SchedulerState::new()),
|
||||||
threads: Arc::new(RefCell::new(VecDeque::new())),
|
threads: Arc::new(RefCell::new(VecDeque::new())),
|
||||||
thread_senders: Arc::new(RefCell::new(HashMap::new())),
|
thread_senders: Arc::new(RefCell::new(HashMap::new())),
|
||||||
futures: Arc::new(AsyncMutex::new(FuturesUnordered::new())),
|
futures: Arc::new(AsyncMutex::new(FuturesUnordered::new())),
|
||||||
}
|
};
|
||||||
|
|
||||||
|
// HACK: Propagate errors given to the scheduler back to their lua threads
|
||||||
|
// FUTURE: Do profiling and anything else we need inside of this interrupt
|
||||||
|
let state = this.state.clone();
|
||||||
|
lua.set_interrupt(move |_| match state.get_lua_error() {
|
||||||
|
Some(e) => Err(e),
|
||||||
|
None => Ok(LuaVmState::Continue),
|
||||||
|
});
|
||||||
|
|
||||||
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
use std::sync::atomic::{AtomicBool, AtomicU8, AtomicUsize, Ordering};
|
use std::{
|
||||||
|
cell::RefCell,
|
||||||
|
sync::atomic::{AtomicBool, AtomicU8, AtomicUsize, Ordering},
|
||||||
|
};
|
||||||
|
|
||||||
|
use mlua::Error as LuaError;
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct SchedulerState {
|
pub struct SchedulerState {
|
||||||
|
@ -6,6 +11,7 @@ pub struct SchedulerState {
|
||||||
exit_code: AtomicU8,
|
exit_code: AtomicU8,
|
||||||
num_resumptions: AtomicUsize,
|
num_resumptions: AtomicUsize,
|
||||||
num_errors: AtomicUsize,
|
num_errors: AtomicUsize,
|
||||||
|
lua_error: RefCell<Option<LuaError>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SchedulerState {
|
impl SchedulerState {
|
||||||
|
@ -41,4 +47,12 @@ impl SchedulerState {
|
||||||
self.exit_state.store(true, Ordering::SeqCst);
|
self.exit_state.store(true, Ordering::SeqCst);
|
||||||
self.exit_code.store(code.into(), Ordering::SeqCst);
|
self.exit_code.store(code.into(), Ordering::SeqCst);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_lua_error(&self) -> Option<LuaError> {
|
||||||
|
self.lua_error.take()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_lua_error(&self, e: LuaError) {
|
||||||
|
self.lua_error.replace(Some(e));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue