Run process.spawn on separate thread, if possible

This commit is contained in:
Filip Tibell 2023-08-20 20:51:42 -05:00
parent 6db01d7b94
commit e3449e7fd9
2 changed files with 32 additions and 23 deletions

View file

@ -1,12 +1,13 @@
use std::{ use std::{
env::{self, consts}, env::{self, consts},
path, path,
process::Stdio, process::{ExitStatus, Stdio},
}; };
use dunce::canonicalize; use dunce::canonicalize;
use mlua::prelude::*; use mlua::prelude::*;
use os_str_bytes::RawOsString; use os_str_bytes::RawOsString;
use tokio::task;
use crate::lune::{scheduler::Scheduler, util::TableBuilder}; use crate::lune::{scheduler::Scheduler, util::TableBuilder};
@ -163,29 +164,20 @@ async fn process_spawn<'lua>(
lua: &'static Lua, lua: &'static Lua,
(program, args, options): (String, Option<Vec<String>>, ProcessSpawnOptions), (program, args, options): (String, Option<Vec<String>>, ProcessSpawnOptions),
) -> LuaResult<LuaTable<'lua>> { ) -> LuaResult<LuaTable<'lua>> {
let inherit_stdio = options.inherit_stdio; // Spawn the new process in the background,
// Spawn the child process // letting the tokio runtime place it on a
let child = options // different thread if necessary, and wait
.into_command(program, args) let (status, stdout, stderr) = task::spawn(spawn_command(program, args, options))
.stdin(Stdio::null()) .await
.stdout(Stdio::piped()) .into_lua_err()??;
.stderr(Stdio::piped())
.spawn()?;
// Inherit the output and stderr if wanted
let result = if inherit_stdio {
pipe_and_inherit_child_process_stdio(child).await
} else {
let output = child.wait_with_output().await?;
Ok((output.status, output.stdout, output.stderr))
};
// Extract result
let (status, stdout, stderr) = result?;
// 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 = status.code().unwrap_or(match stderr.is_empty() { let code = status.code().unwrap_or(match stderr.is_empty() {
true => 0, true => 0,
false => 1, false => 1,
}); });
// Construct and return a readonly lua table with results // Construct and return a readonly lua table with results
TableBuilder::new(lua)? TableBuilder::new(lua)?
.with_value("ok", code == 0)? .with_value("ok", code == 0)?
@ -194,3 +186,25 @@ async fn process_spawn<'lua>(
.with_value("stderr", lua.create_string(&stderr)?)? .with_value("stderr", lua.create_string(&stderr)?)?
.build_readonly() .build_readonly()
} }
async fn spawn_command(
program: String,
args: Option<Vec<String>>,
options: ProcessSpawnOptions,
) -> LuaResult<(ExitStatus, Vec<u8>, Vec<u8>)> {
let inherit_stdio = options.inherit_stdio;
let child = options
.into_command(program, args)
.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()?;
if inherit_stdio {
pipe_and_inherit_child_process_stdio(child).await
} else {
let output = child.wait_with_output().await?;
Ok((output.status, output.stdout, output.stderr))
}
}

View file

@ -83,11 +83,6 @@ assert(#homeDir1 > 0, "Home dir from echo was empty")
assert(#homeDir2 > 0, "Home dir from pwd was empty") assert(#homeDir2 > 0, "Home dir from pwd was empty")
assert(homeDir1 == homeDir2, "Home dirs did not match when performing tilde substitution") assert(homeDir1 == homeDir2, "Home dirs did not match when performing tilde substitution")
-- FIXME: The below tests with sleeping do not work properly and block
-- indefinitely, causing cargo test to timeout, for now we error prematurely
error("FIXME")
--[[ --[[
Spawning a process should not block any lua thread(s) Spawning a process should not block any lua thread(s)