Fix scripts that terminate instantly hanging forever

This commit is contained in:
Filip Tibell 2023-01-24 20:30:47 -05:00
parent 530d01fc9d
commit 915dbf7bd9
No known key found for this signature in database
4 changed files with 41 additions and 49 deletions

View file

@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
### Fixed
- Fixed scripts that terminate instantly sometimes hanging
## `0.1.1` - January 24th, 2023 ## `0.1.1` - January 24th, 2023
### Fixed ### Fixed

View file

@ -1,12 +1,8 @@
use std::{ use std::{env, process::Stdio, sync::Weak};
env,
process::{Command, Stdio},
sync::Weak,
};
use mlua::prelude::*; use mlua::prelude::*;
use os_str_bytes::RawOsString; use os_str_bytes::RawOsString;
use smol::{channel::Sender, unblock}; use smol::{channel::Sender, process::Command};
use crate::{utils::table::TableBuilder, LuneMessage}; use crate::{utils::table::TableBuilder, LuneMessage};
@ -120,24 +116,19 @@ async fn process_spawn(
lua: &Lua, lua: &Lua,
(program, args): (String, Option<Vec<String>>), (program, args): (String, Option<Vec<String>>),
) -> LuaResult<LuaTable> { ) -> LuaResult<LuaTable> {
// Create and spawn a **blocking** child process to prevent // Create and spawn our child process
// issues with yielding across the metamethod/c-call boundary
let pwd = env::current_dir()?; let pwd = env::current_dir()?;
let output = unblock(move || { let mut cmd = Command::new(program);
let mut cmd = Command::new(program); if let Some(args) = args {
if let Some(args) = args { cmd.args(args);
cmd.args(args); }
} let output = cmd
let child = cmd .current_dir(pwd)
.current_dir(pwd) .stdin(Stdio::null())
.stdin(Stdio::null()) .stdout(Stdio::piped())
.stdout(Stdio::piped()) .stderr(Stdio::piped())
.stderr(Stdio::piped()) .output()
.spawn()?; .await?;
child.wait_with_output()
})
.await
.map_err(LuaError::external)?;
// NOTE: If an exit code was not given by the child process, // NOTE: If an exit code was not given by the child process,
// we default to 1 if it yielded any error output, otherwise 0 // we default to 1 if it yielded any error output, otherwise 0
let code = output let code = output

View file

@ -1,6 +1,6 @@
use std::{collections::HashSet, process::ExitCode, sync::Arc}; use std::{collections::HashSet, process::ExitCode, sync::Arc};
use anyhow::{anyhow, bail, Result}; use anyhow::{bail, Result};
use mlua::prelude::*; use mlua::prelude::*;
use smol::LocalExecutor; use smol::LocalExecutor;
@ -12,9 +12,6 @@ use crate::{
utils::formatting::pretty_format_luau_error, utils::formatting::pretty_format_luau_error,
}; };
#[cfg(not(test))]
use crate::utils::formatting::format_label;
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum LuneGlobal { pub enum LuneGlobal {
Console, Console,
@ -43,7 +40,6 @@ pub(crate) enum LuneMessage {
Exit(u8), Exit(u8),
Spawned, Spawned,
Finished, Finished,
Error(anyhow::Error),
LuaError(mlua::Error), LuaError(mlua::Error),
} }
@ -97,28 +93,21 @@ impl Lune {
} }
} }
// Spawn the main thread from our entrypoint script // Spawn the main thread from our entrypoint script
let script_lua = lua.clone();
let script_name = name.to_string(); let script_name = name.to_string();
let script_chunk = chunk.to_string(); let script_chunk = chunk.to_string();
sender.send(LuneMessage::Spawned).await?;
exec.spawn(async move { exec.spawn(async move {
sender.send(LuneMessage::Spawned).await?; let result = script_lua
let result = lua
.load(&script_chunk) .load(&script_chunk)
.set_name(&format!("={}", script_name)) .set_name(&format!("={}", script_name))
.unwrap() .unwrap()
.call_async::<_, LuaMultiValue>(LuaMultiValue::new()) .eval_async::<LuaValue>()
.await; .await;
let message = match result { match result {
Ok(_) => LuneMessage::Finished, Err(e) => sender.send(LuneMessage::LuaError(e)).await,
#[cfg(test)] Ok(_) => sender.send(LuneMessage::Finished).await,
Err(e) => LuneMessage::Error(anyhow!("{}", pretty_format_luau_error(&e))), }
#[cfg(not(test))]
Err(e) => LuneMessage::Error(anyhow!(
"\n{}\n{}",
format_label("ERROR"),
pretty_format_luau_error(&e)
)),
};
sender.send(message).await
}) })
.detach(); .detach();
// Run the executor until there are no tasks left, // Run the executor until there are no tasks left,
@ -153,11 +142,6 @@ impl Lune {
} }
LuneMessage::Spawned => task_count += 1, LuneMessage::Spawned => task_count += 1,
LuneMessage::Finished => task_count -= 1, LuneMessage::Finished => task_count -= 1,
LuneMessage::Error(e) => {
eprintln!("{}", e);
got_error = true;
task_count += 1;
}
LuneMessage::LuaError(e) => { LuneMessage::LuaError(e) => {
eprintln!("{}", pretty_format_luau_error(&e)); eprintln!("{}", pretty_format_luau_error(&e));
got_error = true; got_error = true;
@ -166,6 +150,7 @@ impl Lune {
}; };
// If there are no tasks left running, it is now // If there are no tasks left running, it is now
// safe to close the receiver and end execution // safe to close the receiver and end execution
println!("{}", task_count);
if task_count == 0 { if task_count == 0 {
receiver.close(); receiver.close();
} }

View file

@ -1,4 +1,4 @@
use std::fmt::Debug; use std::fmt::{self, Debug};
use std::sync::Weak; use std::sync::Weak;
use mlua::prelude::*; use mlua::prelude::*;
@ -14,6 +14,16 @@ pub enum TaskRunMode {
Deferred, Deferred,
} }
impl fmt::Display for TaskRunMode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Blocking => write!(f, "Blocking"),
Self::Instant => write!(f, "Instant"),
Self::Deferred => write!(f, "Deferred"),
}
}
}
pub async fn run_registered_task<T>( pub async fn run_registered_task<T>(
lua: &Lua, lua: &Lua,
mode: TaskRunMode, mode: TaskRunMode,