From 6c49ef7e4ad7ca9796ecaa4a650a8e16713acf09 Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Sat, 11 Feb 2023 12:39:39 +0100 Subject: [PATCH] Use static lua lifetime --- packages/lib/src/globals/fs.rs | 18 ++++----- packages/lib/src/globals/mod.rs | 4 +- packages/lib/src/globals/net.rs | 32 +++++++-------- packages/lib/src/globals/process.rs | 19 +++++---- packages/lib/src/globals/require.rs | 2 +- packages/lib/src/globals/stdio.rs | 4 +- packages/lib/src/globals/task.rs | 56 ++++++++++++--------------- packages/lib/src/globals/top_level.rs | 2 +- packages/lib/src/lib.rs | 12 +++--- packages/lib/src/utils/process.rs | 2 +- packages/lib/src/utils/task.rs | 2 +- 11 files changed, 74 insertions(+), 79 deletions(-) diff --git a/packages/lib/src/globals/fs.rs b/packages/lib/src/globals/fs.rs index 1846fd1..11a5af3 100644 --- a/packages/lib/src/globals/fs.rs +++ b/packages/lib/src/globals/fs.rs @@ -5,7 +5,7 @@ use tokio::fs; use crate::utils::table::TableBuilder; -pub fn create(lua: &Lua) -> LuaResult { +pub fn create(lua: &'static Lua) -> LuaResult { TableBuilder::new(lua)? .with_async_function("readFile", fs_read_file)? .with_async_function("readDir", fs_read_dir)? @@ -18,11 +18,11 @@ pub fn create(lua: &Lua) -> LuaResult { .build_readonly() } -async fn fs_read_file(_: &Lua, path: String) -> LuaResult { +async fn fs_read_file(_: &'static Lua, path: String) -> LuaResult { fs::read_to_string(&path).await.map_err(LuaError::external) } -async fn fs_read_dir(_: &Lua, path: String) -> LuaResult> { +async fn fs_read_dir(_: &'static Lua, path: String) -> LuaResult> { let mut dir_strings = Vec::new(); let mut dir = fs::read_dir(&path).await.map_err(LuaError::external)?; while let Some(dir_entry) = dir.next_entry().await.map_err(LuaError::external)? { @@ -52,25 +52,25 @@ async fn fs_read_dir(_: &Lua, path: String) -> LuaResult> { Ok(dir_strings_no_prefix) } -async fn fs_write_file(_: &Lua, (path, contents): (String, String)) -> LuaResult<()> { +async fn fs_write_file(_: &'static Lua, (path, contents): (String, String)) -> LuaResult<()> { fs::write(&path, &contents) .await .map_err(LuaError::external) } -async fn fs_write_dir(_: &Lua, path: String) -> LuaResult<()> { +async fn fs_write_dir(_: &'static Lua, path: String) -> LuaResult<()> { fs::create_dir_all(&path).await.map_err(LuaError::external) } -async fn fs_remove_file(_: &Lua, path: String) -> LuaResult<()> { +async fn fs_remove_file(_: &'static Lua, path: String) -> LuaResult<()> { fs::remove_file(&path).await.map_err(LuaError::external) } -async fn fs_remove_dir(_: &Lua, path: String) -> LuaResult<()> { +async fn fs_remove_dir(_: &'static Lua, path: String) -> LuaResult<()> { fs::remove_dir_all(&path).await.map_err(LuaError::external) } -async fn fs_is_file(_: &Lua, path: String) -> LuaResult { +async fn fs_is_file(_: &'static Lua, path: String) -> LuaResult { let path = PathBuf::from(path); if path.exists() { Ok(fs::metadata(path) @@ -82,7 +82,7 @@ async fn fs_is_file(_: &Lua, path: String) -> LuaResult { } } -async fn fs_is_dir(_: &Lua, path: String) -> LuaResult { +async fn fs_is_dir(_: &'static Lua, path: String) -> LuaResult { let path = PathBuf::from(path); if path.exists() { Ok(fs::metadata(path) diff --git a/packages/lib/src/globals/mod.rs b/packages/lib/src/globals/mod.rs index c728006..a0b27de 100644 --- a/packages/lib/src/globals/mod.rs +++ b/packages/lib/src/globals/mod.rs @@ -78,7 +78,7 @@ impl LuneGlobal { Note that proxy globals should be handled with special care and that [`LuneGlobal::inject()`] should be preferred over manually creating and manipulating the value(s) of any Lune global. */ - pub fn value<'a>(&'a self, lua: &'a Lua) -> LuaResult { + pub fn value(&self, lua: &'static Lua) -> LuaResult { match self { LuneGlobal::Fs => fs::create(lua), LuneGlobal::Net => net::create(lua), @@ -98,7 +98,7 @@ impl LuneGlobal { Refer to [`LuneGlobal::is_top_level()`] for more info on proxy globals. */ - pub fn inject(self, lua: &Lua) -> LuaResult<()> { + pub fn inject(self, lua: &'static Lua) -> LuaResult<()> { let globals = lua.globals(); let table = self.value(lua)?; // NOTE: Top level globals are special, the values diff --git a/packages/lib/src/globals/net.rs b/packages/lib/src/globals/net.rs index d3944c0..595f1bb 100644 --- a/packages/lib/src/globals/net.rs +++ b/packages/lib/src/globals/net.rs @@ -19,7 +19,7 @@ use crate::utils::{ table::TableBuilder, }; -pub fn create(lua: &Lua) -> LuaResult { +pub fn create(lua: &'static Lua) -> LuaResult { // Create a reusable client for performing our // web requests and store it in the lua registry let mut default_headers = HeaderMap::new(); @@ -43,7 +43,7 @@ pub fn create(lua: &Lua) -> LuaResult { .build_readonly() } -fn net_json_encode(_: &Lua, (val, pretty): (LuaValue, Option)) -> LuaResult { +fn net_json_encode(_: &'static Lua, (val, pretty): (LuaValue, Option)) -> LuaResult { if let Some(true) = pretty { serde_json::to_string_pretty(&val).map_err(LuaError::external) } else { @@ -51,12 +51,12 @@ fn net_json_encode(_: &Lua, (val, pretty): (LuaValue, Option)) -> LuaResul } } -fn net_json_decode(lua: &Lua, json: String) -> LuaResult { +fn net_json_decode(lua: &'static Lua, json: String) -> LuaResult { let json: serde_json::Value = serde_json::from_str(&json).map_err(LuaError::external)?; lua.to_value(&json) } -async fn net_request<'lua>(lua: &'lua Lua, config: LuaValue<'lua>) -> LuaResult> { +async fn net_request<'a>(lua: &'static Lua, config: LuaValue<'a>) -> LuaResult> { let client: NetClient = lua.named_registry_value("NetClient")?; // Extract stuff from config and make sure its all valid let (url, method, headers, body) = match config { @@ -147,20 +147,16 @@ async fn net_request<'lua>(lua: &'lua Lua, config: LuaValue<'lua>) -> LuaResult< .build_readonly() } -async fn net_serve<'lua>( - lua: &'lua Lua, - (port, callback): (u16, LuaFunction<'lua>), -) -> LuaResult<()> { - let server_lua = lua.app_data_ref::>().unwrap().upgrade().unwrap(); +async fn net_serve(lua: &'static Lua, (port, callback): (u16, LuaFunction<'_>)) -> LuaResult<()> { let server_sender = lua .app_data_ref::>>() .unwrap() .upgrade() .unwrap(); - let server_callback = server_lua.create_registry_value(callback)?; + let server_callback = lua.create_registry_value(callback)?; let server = Server::bind(&([127, 0, 0, 1], port).into()) .executor(LocalExec) - .serve(MakeNetService(server_lua, server_callback.into())); + .serve(MakeNetService(lua, server_callback.into())); if let Err(err) = server.await.map_err(LuaError::external) { server_sender .send(LuneMessage::LuaError(err)) @@ -173,7 +169,7 @@ async fn net_serve<'lua>( // Hyper service implementation for net, lots of boilerplate here // but make_svc and make_svc_function do not work for what we need -pub struct NetService(Arc, Arc); +pub struct NetService(&'static Lua, Arc); impl Service> for NetService { type Response = Response; @@ -185,7 +181,7 @@ impl Service> for NetService { } fn call(&mut self, req: Request) -> Self::Future { - let lua = self.0.clone(); + let lua = self.0; let key = self.1.clone(); let (parts, body) = req.into_parts(); Box::pin(async move { @@ -199,7 +195,7 @@ impl Service> for NetService { .upgrade() .unwrap(); // Create a readonly table for the request query params - let query_params = TableBuilder::new(&lua)? + let query_params = TableBuilder::new(lua)? .with_values( parts .uri @@ -211,7 +207,7 @@ impl Service> for NetService { )? .build_readonly()?; // Do the same for headers - let header_map = TableBuilder::new(&lua)? + let header_map = TableBuilder::new(lua)? .with_values( parts .headers @@ -223,7 +219,7 @@ impl Service> for NetService { )? .build_readonly()?; // Create a readonly table with request info to pass to the handler - let request = TableBuilder::new(&lua)? + let request = TableBuilder::new(lua)? .with_value("path", parts.uri.path())? .with_value("query", query_params)? .with_value("method", parts.method.as_str())? @@ -287,7 +283,7 @@ impl Service> for NetService { } } -struct MakeNetService(Arc, Arc); +struct MakeNetService(&'static Lua, Arc); impl Service<&AddrStream> for MakeNetService { type Response = NetService; @@ -299,7 +295,7 @@ impl Service<&AddrStream> for MakeNetService { } fn call(&mut self, _: &AddrStream) -> Self::Future { - let lua = self.0.clone(); + let lua = self.0; let key = self.1.clone(); Box::pin(async move { Ok(NetService(lua, key)) }) } diff --git a/packages/lib/src/globals/process.rs b/packages/lib/src/globals/process.rs index 0ea009f..b6215e5 100644 --- a/packages/lib/src/globals/process.rs +++ b/packages/lib/src/globals/process.rs @@ -10,7 +10,7 @@ use crate::utils::{ table::TableBuilder, }; -pub fn create(lua: &Lua, args_vec: Vec) -> LuaResult { +pub fn create(lua: &'static Lua, args_vec: Vec) -> LuaResult { let cwd = env::current_dir()?.canonicalize()?; let mut cwd_str = cwd.to_string_lossy().to_string(); if !cwd_str.ends_with('/') { @@ -40,10 +40,10 @@ pub fn create(lua: &Lua, args_vec: Vec) -> LuaResult { .build_readonly() } -fn process_env_get<'lua>( - lua: &'lua Lua, - (_, key): (LuaValue<'lua>, String), -) -> LuaResult> { +fn process_env_get<'a>( + lua: &'static Lua, + (_, key): (LuaValue<'a>, String), +) -> LuaResult> { match env::var_os(key) { Some(value) => { let raw_value = RawOsString::new(value); @@ -55,7 +55,10 @@ fn process_env_get<'lua>( } } -fn process_env_set(_: &Lua, (_, key, value): (LuaValue, String, Option)) -> LuaResult<()> { +fn process_env_set( + _: &'static Lua, + (_, key, value): (LuaValue, String, Option), +) -> LuaResult<()> { // Make sure key is valid, otherwise set_var will panic if key.is_empty() { Err(LuaError::RuntimeError("Key must not be empty".to_string())) @@ -106,12 +109,12 @@ fn process_env_iter<'lua>( }) } -async fn process_exit(lua: &Lua, exit_code: Option) -> LuaResult<()> { +async fn process_exit(lua: &'static Lua, exit_code: Option) -> LuaResult<()> { exit_and_yield_forever(lua, exit_code).await } async fn process_spawn<'a>( - lua: &'a Lua, + lua: &'static Lua, (mut program, args, options): (String, Option>, Option>), ) -> LuaResult> { // Parse any given options or create defaults diff --git a/packages/lib/src/globals/require.rs b/packages/lib/src/globals/require.rs index f8cf684..a666043 100644 --- a/packages/lib/src/globals/require.rs +++ b/packages/lib/src/globals/require.rs @@ -9,7 +9,7 @@ use os_str_bytes::{OsStrBytes, RawOsStr}; use crate::utils::table::TableBuilder; -pub fn create(lua: &Lua) -> LuaResult { +pub fn create(lua: &'static Lua) -> LuaResult { let require: LuaFunction = lua.globals().raw_get("require")?; // Preserve original require behavior if we have a special env var set if env::var_os("LUAU_PWD_REQUIRE").is_some() { diff --git a/packages/lib/src/globals/stdio.rs b/packages/lib/src/globals/stdio.rs index b39d929..bd8cffb 100644 --- a/packages/lib/src/globals/stdio.rs +++ b/packages/lib/src/globals/stdio.rs @@ -8,7 +8,7 @@ use crate::utils::{ table::TableBuilder, }; -pub fn create(lua: &Lua) -> LuaResult { +pub fn create(lua: &'static Lua) -> LuaResult { TableBuilder::new(lua)? .with_function("color", |_, color: String| { let ansi_string = format_style(style_from_color_str(&color)?); @@ -38,7 +38,7 @@ fn prompt_theme() -> ColorfulTheme { } fn prompt<'a>( - lua: &'a Lua, + lua: &'static Lua, (kind, message, options): (Option, Option, LuaValue<'a>), ) -> LuaResult> { match kind.map(|k| k.trim().to_ascii_lowercase()).as_deref() { diff --git a/packages/lib/src/globals/task.rs b/packages/lib/src/globals/task.rs index f429423..05c4c37 100644 --- a/packages/lib/src/globals/task.rs +++ b/packages/lib/src/globals/task.rs @@ -1,7 +1,4 @@ -use std::{ - sync::Weak, - time::{Duration, Instant}, -}; +use std::time::{Duration, Instant}; use mlua::prelude::*; use tokio::time; @@ -13,7 +10,7 @@ use crate::utils::{ const MINIMUM_WAIT_OR_DELAY_DURATION: f32 = 10.0 / 1_000.0; // 10ms -pub fn create(lua: &Lua) -> LuaResult { +pub fn create(lua: &'static Lua) -> LuaResult { // HACK: There is no way to call coroutine.close directly from the mlua // crate, so we need to fetch the function and store it in the registry let coroutine: LuaTable = lua.globals().raw_get("coroutine")?; @@ -33,8 +30,11 @@ pub fn create(lua: &Lua) -> LuaResult { .build_readonly() } -fn tof_to_thread<'a>(lua: &'a Lua, tof: LuaValue<'a>) -> LuaResult> { - match tof { +fn tof_to_thread<'a>( + lua: &'static Lua, + thread_or_function: LuaValue<'a>, +) -> LuaResult> { + match thread_or_function { LuaValue::Thread(t) => Ok(t), LuaValue::Function(f) => Ok(lua.create_thread(f)?), value => Err(LuaError::RuntimeError(format!( @@ -44,31 +44,29 @@ fn tof_to_thread<'a>(lua: &'a Lua, tof: LuaValue<'a>) -> LuaResult } } -async fn task_cancel<'a>(lua: &'a Lua, thread: LuaThread<'a>) -> LuaResult<()> { +async fn task_cancel<'a>(lua: &'static Lua, thread: LuaThread<'a>) -> LuaResult<()> { let close: LuaFunction = lua.named_registry_value("coroutine.close")?; close.call_async::<_, LuaMultiValue>(thread).await?; Ok(()) } async fn task_defer<'a>( - lua: &'a Lua, + lua: &'static Lua, (tof, args): (LuaValue<'a>, LuaMultiValue<'a>), ) -> LuaResult> { - // Spawn a new detached task using a lua reference that we can use inside of our task - let task_lua = lua.app_data_ref::>().unwrap().upgrade().unwrap(); let task_thread = tof_to_thread(lua, tof)?; let task_thread_key = lua.create_registry_value(task_thread)?; let task_args_key = lua.create_registry_value(args.into_vec())?; let lua_thread_to_return = lua.registry_value(&task_thread_key)?; run_registered_task(lua, TaskRunMode::Deferred, async move { - let thread: LuaThread = task_lua.registry_value(&task_thread_key)?; - let argsv: Vec = task_lua.registry_value(&task_args_key)?; + let thread: LuaThread = lua.registry_value(&task_thread_key)?; + let argsv: Vec = lua.registry_value(&task_args_key)?; let args = LuaMultiValue::from_vec(argsv); if thread.status() == LuaThreadStatus::Resumable { let _: LuaMultiValue = thread.into_async(args).await?; } - task_lua.remove_registry_value(task_thread_key)?; - task_lua.remove_registry_value(task_args_key)?; + lua.remove_registry_value(task_thread_key)?; + lua.remove_registry_value(task_args_key)?; Ok(()) }) .await?; @@ -76,25 +74,23 @@ async fn task_defer<'a>( } async fn task_delay<'a>( - lua: &'a Lua, + lua: &'static Lua, (duration, tof, args): (Option, LuaValue<'a>, LuaMultiValue<'a>), ) -> LuaResult> { - // Spawn a new detached task using a lua reference that we can use inside of our task - let task_lua = lua.app_data_ref::>().unwrap().upgrade().unwrap(); let task_thread = tof_to_thread(lua, tof)?; let task_thread_key = lua.create_registry_value(task_thread)?; let task_args_key = lua.create_registry_value(args.into_vec())?; let lua_thread_to_return = lua.registry_value(&task_thread_key)?; run_registered_task(lua, TaskRunMode::Deferred, async move { - task_wait(&task_lua, duration).await?; - let thread: LuaThread = task_lua.registry_value(&task_thread_key)?; - let argsv: Vec = task_lua.registry_value(&task_args_key)?; + task_wait(lua, duration).await?; + let thread: LuaThread = lua.registry_value(&task_thread_key)?; + let argsv: Vec = lua.registry_value(&task_args_key)?; let args = LuaMultiValue::from_vec(argsv); if thread.status() == LuaThreadStatus::Resumable { let _: LuaMultiValue = thread.into_async(args).await?; } - task_lua.remove_registry_value(task_thread_key)?; - task_lua.remove_registry_value(task_args_key)?; + lua.remove_registry_value(task_thread_key)?; + lua.remove_registry_value(task_args_key)?; Ok(()) }) .await?; @@ -102,31 +98,29 @@ async fn task_delay<'a>( } async fn task_spawn<'a>( - lua: &'a Lua, + lua: &'static Lua, (tof, args): (LuaValue<'a>, LuaMultiValue<'a>), ) -> LuaResult> { - // Spawn a new detached task using a lua reference that we can use inside of our task - let task_lua = lua.app_data_ref::>().unwrap().upgrade().unwrap(); let task_thread = tof_to_thread(lua, tof)?; let task_thread_key = lua.create_registry_value(task_thread)?; let task_args_key = lua.create_registry_value(args.into_vec())?; let lua_thread_to_return = lua.registry_value(&task_thread_key)?; run_registered_task(lua, TaskRunMode::Instant, async move { - let thread: LuaThread = task_lua.registry_value(&task_thread_key)?; - let argsv: Vec = task_lua.registry_value(&task_args_key)?; + let thread: LuaThread = lua.registry_value(&task_thread_key)?; + let argsv: Vec = lua.registry_value(&task_args_key)?; let args = LuaMultiValue::from_vec(argsv); if thread.status() == LuaThreadStatus::Resumable { let _: LuaMultiValue = thread.into_async(args).await?; } - task_lua.remove_registry_value(task_thread_key)?; - task_lua.remove_registry_value(task_args_key)?; + lua.remove_registry_value(task_thread_key)?; + lua.remove_registry_value(task_args_key)?; Ok(()) }) .await?; Ok(lua_thread_to_return) } -async fn task_wait(lua: &Lua, duration: Option) -> LuaResult { +async fn task_wait(lua: &'static Lua, duration: Option) -> LuaResult { let start = Instant::now(); run_registered_task(lua, TaskRunMode::Blocking, async move { time::sleep(Duration::from_secs_f32( diff --git a/packages/lib/src/globals/top_level.rs b/packages/lib/src/globals/top_level.rs index caea783..dc61f9f 100644 --- a/packages/lib/src/globals/top_level.rs +++ b/packages/lib/src/globals/top_level.rs @@ -5,7 +5,7 @@ use crate::utils::{ table::TableBuilder, }; -pub fn create(lua: &Lua) -> LuaResult { +pub fn create(lua: &'static Lua) -> LuaResult { let globals = lua.globals(); // HACK: We need to preserve the default behavior of the // print and error functions, for pcall and such, which diff --git a/packages/lib/src/lib.rs b/packages/lib/src/lib.rs index aa1e42a..e644d13 100644 --- a/packages/lib/src/lib.rs +++ b/packages/lib/src/lib.rs @@ -76,6 +76,10 @@ impl Lune { Some Lune globals such as [`LuneGlobal::Process`] may spawn separate tokio tasks on other threads, but the Luau environment itself is guaranteed to run on a single thread in the local set. + + Note that this will create a static Lua instance that will live + for the remainer of the program, and that this leaks memory using + [`Box::leak`] that will then get deallocated when the program exits. */ pub async fn run( &self, @@ -84,18 +88,16 @@ impl Lune { ) -> Result { let task_set = task::LocalSet::new(); let (sender, mut receiver) = mpsc::channel::(64); - let lua = Arc::new(mlua::Lua::new()); + let lua = Lua::new().into_static(); let snd = Arc::new(sender); - lua.set_app_data(Arc::downgrade(&lua)); lua.set_app_data(Arc::downgrade(&snd)); // Add in wanted lune globals for global in self.includes.clone() { if !self.excludes.contains(&global) { - global.inject(&lua)?; + global.inject(lua)?; } } // Spawn the main thread from our entrypoint script - let script_lua = lua.clone(); let script_name = script_name.to_string(); let script_chunk = script_contents.to_string(); let script_sender = snd.clone(); @@ -104,7 +106,7 @@ impl Lune { .await .map_err(LuaError::external)?; task_set.spawn_local(async move { - let result = script_lua + let result = lua .load(&script_chunk) .set_name(&format!("={script_name}")) .unwrap() diff --git a/packages/lib/src/utils/process.rs b/packages/lib/src/utils/process.rs index e08f214..e87991e 100644 --- a/packages/lib/src/utils/process.rs +++ b/packages/lib/src/utils/process.rs @@ -41,7 +41,7 @@ pub async fn pipe_and_inherit_child_process_stdio( Ok::<_, LuaError>((status, stdout_buffer?, stderr_buffer?)) } -pub async fn exit_and_yield_forever(lua: &Lua, exit_code: Option) -> LuaResult<()> { +pub async fn exit_and_yield_forever(lua: &'static Lua, exit_code: Option) -> LuaResult<()> { let sender = lua .app_data_ref::>>() .unwrap() diff --git a/packages/lib/src/utils/task.rs b/packages/lib/src/utils/task.rs index 8a14f23..dd4ff7c 100644 --- a/packages/lib/src/utils/task.rs +++ b/packages/lib/src/utils/task.rs @@ -26,7 +26,7 @@ impl fmt::Display for TaskRunMode { } pub async fn run_registered_task( - lua: &Lua, + lua: &'static Lua, mode: TaskRunMode, to_run: impl Future> + 'static, ) -> LuaResult<()> {