diff --git a/crates/lune-std/src/globals/require/context.rs b/crates/lune-std/src/globals/require/context.rs index 7072c41..b3f7927 100644 --- a/crates/lune-std/src/globals/require/context.rs +++ b/crates/lune-std/src/globals/require/context.rs @@ -3,7 +3,10 @@ use mlua::prelude::*; use std::{collections::HashMap, path::PathBuf, sync::Arc}; use tokio::{ fs, - sync::{broadcast::Sender, Mutex}, + sync::{ + broadcast::{self, Sender}, + Mutex, + }, }; /// The private struct that's stored in mlua's app data container @@ -78,6 +81,48 @@ impl RequireContext { path_rel: PathBuf, path_abs: PathBuf, ) -> LuaResult { + // wait for module to be required + // if its pending somewhere else + { + let data_ref = lua + .app_data_ref::() + .ok_or(LuaError::runtime("Couldn't find RequireContextData in app data container, make sure RequireStorage::init is called on this lua instance"))?; + + let pending = data_ref.pending.try_lock().into_lua_err()?; + + if let Some(a) = pending.get(&path_abs) { + a.subscribe().recv().await.into_lua_err()?; + } + } + + // get module from cache + // *if* its cached + { + let data_ref = lua + .app_data_ref::() + .ok_or(LuaError::runtime("Couldn't find RequireContextData in app data container, make sure RequireStorage::init is called on this lua instance"))?; + + let cache = data_ref.cache.lock().await; + + if let Some(cached) = cache.get(&path_abs) { + return cached.into_lua(lua).into_lua_multi(lua); + } + } + + // create a broadcast channel + { + let data_ref = lua + .app_data_ref::() + .ok_or(LuaError::runtime("Couldn't find RequireContextData in app data container, make sure RequireStorage::init is called on this lua instance"))?; + + let (broadcast_tx, _) = broadcast::channel(1); + + { + let mut pending = data_ref.pending.try_lock().into_lua_err()?; + pending.insert(path_abs.clone(), broadcast_tx); + } + } + if !fs::try_exists(&path_abs).await? { return Err(LuaError::runtime(format!( "Can not require '{}' as it does not exist", @@ -87,10 +132,35 @@ impl RequireContext { let content = fs::read_to_string(&path_abs).await?; - lua.load(content) + let multi = lua + .load(content) .set_name(path_abs.to_string_lossy()) - .eval_async() + .eval_async::() + .await?; + + let mutli_clone = multi.clone(); + let multi_reg = lua.create_registry_value(mutli_clone.into_vec())?; + + let data_ref = lua + .app_data_ref::() + .ok_or(LuaError::runtime("Couldn't find RequireContextData in app data container, make sure RequireStorage::init is called on this lua instance"))?; + + data_ref + .cache + .lock() .await + .insert(path_abs.clone(), multi_reg); + + let broadcast_tx = data_ref + .pending + .lock() + .await + .remove(&path_abs) + .expect("Pending require broadcaster was unexpectedly removed"); + + broadcast_tx.send(()).ok(); + + Ok(multi) } pub fn inject_std(