mirror of
https://github.com/lune-org/lune.git
synced 2025-01-05 19:09:10 +00:00
Initial work on async impl of process spawn
This commit is contained in:
parent
7aa8896997
commit
72f6b356c4
6 changed files with 99 additions and 66 deletions
11
Cargo.lock
generated
11
Cargo.lock
generated
|
@ -583,6 +583,7 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tokio-pipe",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1143,6 +1144,16 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-pipe"
|
||||||
|
version = "0.2.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f213a84bffbd61b8fa0ba8a044b4bbe35d471d0b518867181e82bd5c15542784"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-rustls"
|
name = "tokio-rustls"
|
||||||
version = "0.23.4"
|
version = "0.23.4"
|
||||||
|
|
|
@ -24,6 +24,7 @@ console = "0.15.5"
|
||||||
dialoguer = "0.10.3"
|
dialoguer = "0.10.3"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
os_str_bytes = "6.4.1"
|
os_str_bytes = "6.4.1"
|
||||||
|
tokio-pipe = "0.2.12"
|
||||||
|
|
||||||
hyper = { version = "0.14.24", features = ["full"] }
|
hyper = { version = "0.14.24", features = ["full"] }
|
||||||
mlua = { version = "0.8.7", features = ["luau", "async", "serialize"] }
|
mlua = { version = "0.8.7", features = ["luau", "async", "serialize"] }
|
||||||
|
|
|
@ -1,12 +1,8 @@
|
||||||
use std::{
|
use std::{collections::HashMap, env, path::PathBuf, process::Stdio};
|
||||||
collections::HashMap,
|
|
||||||
env,
|
|
||||||
path::PathBuf,
|
|
||||||
process::{Command, Stdio},
|
|
||||||
};
|
|
||||||
|
|
||||||
use mlua::prelude::*;
|
use mlua::prelude::*;
|
||||||
use os_str_bytes::RawOsString;
|
use os_str_bytes::RawOsString;
|
||||||
|
use tokio::process::Command;
|
||||||
|
|
||||||
use crate::utils::{
|
use crate::utils::{
|
||||||
process::{exit_and_yield_forever, pipe_and_inherit_child_process_stdio},
|
process::{exit_and_yield_forever, pipe_and_inherit_child_process_stdio},
|
||||||
|
@ -17,7 +13,7 @@ pub fn create(lua: &Lua, args_vec: Vec<String>) -> LuaResult<()> {
|
||||||
let cwd = env::current_dir()?.canonicalize()?;
|
let cwd = env::current_dir()?.canonicalize()?;
|
||||||
let mut cwd_str = cwd.to_string_lossy().to_string();
|
let mut cwd_str = cwd.to_string_lossy().to_string();
|
||||||
if !cwd_str.ends_with('/') {
|
if !cwd_str.ends_with('/') {
|
||||||
cwd_str = format!("{}/", cwd_str);
|
cwd_str = format!("{cwd_str}/");
|
||||||
}
|
}
|
||||||
// Create readonly args array
|
// Create readonly args array
|
||||||
let args_tab = TableBuilder::new(lua)?
|
let args_tab = TableBuilder::new(lua)?
|
||||||
|
@ -41,7 +37,7 @@ pub fn create(lua: &Lua, args_vec: Vec<String>) -> LuaResult<()> {
|
||||||
.with_value("cwd", cwd_str)?
|
.with_value("cwd", cwd_str)?
|
||||||
.with_value("env", env_tab)?
|
.with_value("env", env_tab)?
|
||||||
.with_async_function("exit", process_exit)?
|
.with_async_function("exit", process_exit)?
|
||||||
.with_function("spawn", process_spawn)?
|
.with_async_function("spawn", process_spawn)?
|
||||||
.build_readonly()?,
|
.build_readonly()?,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -116,7 +112,7 @@ async fn process_exit(lua: &Lua, exit_code: Option<u8>) -> LuaResult<()> {
|
||||||
exit_and_yield_forever(lua, exit_code).await
|
exit_and_yield_forever(lua, exit_code).await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_spawn<'a>(
|
async fn process_spawn<'a>(
|
||||||
lua: &'a Lua,
|
lua: &'a Lua,
|
||||||
(mut program, args, options): (String, Option<Vec<String>>, Option<LuaTable<'a>>),
|
(mut program, args, options): (String, Option<Vec<String>>, Option<LuaTable<'a>>),
|
||||||
) -> LuaResult<LuaTable<'a>> {
|
) -> LuaResult<LuaTable<'a>> {
|
||||||
|
@ -233,9 +229,9 @@ fn process_spawn<'a>(
|
||||||
.spawn()?;
|
.spawn()?;
|
||||||
// Inherit the output and stderr if wanted
|
// Inherit the output and stderr if wanted
|
||||||
let result = if child_stdio_inherit {
|
let result = if child_stdio_inherit {
|
||||||
pipe_and_inherit_child_process_stdio(child)
|
pipe_and_inherit_child_process_stdio(child).await
|
||||||
} else {
|
} else {
|
||||||
let output = child.wait_with_output()?;
|
let output = child.wait_with_output().await?;
|
||||||
Ok((output.status, output.stdout, output.stderr))
|
Ok((output.status, output.stdout, output.stderr))
|
||||||
};
|
};
|
||||||
// Extract result
|
// Extract result
|
||||||
|
|
|
@ -106,7 +106,7 @@ impl Lune {
|
||||||
task_set.spawn_local(async move {
|
task_set.spawn_local(async move {
|
||||||
let result = script_lua
|
let result = script_lua
|
||||||
.load(&script_chunk)
|
.load(&script_chunk)
|
||||||
.set_name(&format!("={}", script_name))
|
.set_name(&format!("={script_name}"))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.eval_async::<LuaValue>()
|
.eval_async::<LuaValue>()
|
||||||
.await;
|
.await;
|
||||||
|
@ -132,8 +132,7 @@ impl Lune {
|
||||||
message => {
|
message => {
|
||||||
if task_count == 0 {
|
if task_count == 0 {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"Got message while task count was 0!\nMessage: {:#?}",
|
"Got message while task count was 0!\nMessage: {message:#?}"
|
||||||
message
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -242,19 +242,13 @@ pub fn pretty_format_luau_error(e: &LuaError) -> String {
|
||||||
let msg = message
|
let msg = message
|
||||||
.clone()
|
.clone()
|
||||||
.map_or_else(String::new, |m| format!("\nDetails:\n\t{m}"));
|
.map_or_else(String::new, |m| format!("\nDetails:\n\t{m}"));
|
||||||
format!(
|
format!("Failed to convert Rust type '{from}' into Luau type '{to}'!{msg}")
|
||||||
"Failed to convert Rust type '{}' into Luau type '{}'!{}",
|
|
||||||
from, to, msg
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
LuaError::FromLuaConversionError { from, to, message } => {
|
LuaError::FromLuaConversionError { from, to, message } => {
|
||||||
let msg = message
|
let msg = message
|
||||||
.clone()
|
.clone()
|
||||||
.map_or_else(String::new, |m| format!("\nDetails:\n\t{m}"));
|
.map_or_else(String::new, |m| format!("\nDetails:\n\t{m}"));
|
||||||
format!(
|
format!("Failed to convert Luau type '{from}' into Rust type '{to}'!{msg}")
|
||||||
"Failed to convert Luau type '{}' into Rust type '{}'!{}",
|
|
||||||
from, to, msg
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
e => format!("{e}"),
|
e => format!("{e}"),
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,81 +1,113 @@
|
||||||
// https://stackoverflow.com/questions/71141122/-
|
// https://stackoverflow.com/questions/71141122/-
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
io,
|
pin::Pin,
|
||||||
io::Write,
|
process::ExitStatus,
|
||||||
process::{Child, ExitStatus},
|
|
||||||
sync::Weak,
|
sync::Weak,
|
||||||
|
task::{Context, Poll},
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
use mlua::prelude::*;
|
use mlua::prelude::*;
|
||||||
use tokio::{sync::mpsc::Sender, time};
|
use tokio::{
|
||||||
|
io::{self, AsyncWrite, AsyncWriteExt},
|
||||||
|
process::Child,
|
||||||
|
sync::mpsc::Sender,
|
||||||
|
task, time,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::LuneMessage;
|
use crate::LuneMessage;
|
||||||
|
|
||||||
pub struct TeeWriter<'a, W0: Write, W1: Write> {
|
pub struct TeeWriter<'a, L, R>
|
||||||
w0: &'a mut W0,
|
where
|
||||||
w1: &'a mut W1,
|
L: AsyncWrite + Unpin,
|
||||||
|
R: AsyncWrite + Unpin,
|
||||||
|
{
|
||||||
|
left: &'a mut L,
|
||||||
|
right: &'a mut R,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, W0: Write, W1: Write> TeeWriter<'a, W0, W1> {
|
impl<'a, L, R> TeeWriter<'a, L, R>
|
||||||
pub fn new(w0: &'a mut W0, w1: &'a mut W1) -> Self {
|
where
|
||||||
Self { w0, w1 }
|
L: AsyncWrite + Unpin,
|
||||||
|
R: AsyncWrite + Unpin,
|
||||||
|
{
|
||||||
|
pub fn new(left: &'a mut L, right: &'a mut R) -> Self {
|
||||||
|
Self { left, right }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, W0: Write, W1: Write> Write for TeeWriter<'a, W0, W1> {
|
impl<'a, L, R> AsyncWrite for TeeWriter<'a, L, R>
|
||||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
where
|
||||||
// We have to use write_all() otherwise what
|
L: AsyncWrite + Unpin,
|
||||||
// happens if different amounts are written?
|
R: AsyncWrite + Unpin,
|
||||||
self.w0.write_all(buf)?;
|
{
|
||||||
self.w1.write_all(buf)?;
|
fn poll_write(
|
||||||
Ok(buf.len())
|
mut self: Pin<&mut Self>,
|
||||||
|
_: &mut Context<'_>,
|
||||||
|
buf: &[u8],
|
||||||
|
) -> Poll<io::Result<usize>> {
|
||||||
|
// TODO: Figure out how to poll both of these
|
||||||
|
// futures, we can't use await in this trait impl
|
||||||
|
// It might be better to split the generic TeeWriter out
|
||||||
|
// and instead make TeeStdoutWriter and TeeStderrWriter
|
||||||
|
// structs that use Stdout and Stderr + Vec directly,
|
||||||
|
// all of which already implement these traits for us
|
||||||
|
self.left.write_all(buf);
|
||||||
|
self.right.write_all(buf);
|
||||||
|
Poll::Ready(Ok(buf.len()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flush(&mut self) -> io::Result<()> {
|
fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||||
self.w0.flush()?;
|
todo!()
|
||||||
self.w1.flush()?;
|
}
|
||||||
Ok(())
|
|
||||||
|
fn poll_shutdown(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||||
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pipe_and_inherit_child_process_stdio(
|
pub async fn pipe_and_inherit_child_process_stdio(
|
||||||
mut child: Child,
|
mut child: Child,
|
||||||
) -> LuaResult<(ExitStatus, Vec<u8>, Vec<u8>)> {
|
) -> LuaResult<(ExitStatus, Vec<u8>, Vec<u8>)> {
|
||||||
// https://stackoverflow.com/questions/71141122/-
|
// https://stackoverflow.com/questions/71141122/-
|
||||||
let mut child_stdout = child.stdout.take().unwrap();
|
let mut child_stdout = child.stdout.take().unwrap();
|
||||||
let mut child_stderr = child.stderr.take().unwrap();
|
let mut child_stderr = child.stderr.take().unwrap();
|
||||||
std::thread::scope(|s| {
|
|
||||||
let stdout_thread = s.spawn(|| {
|
|
||||||
let stdout = io::stdout();
|
|
||||||
let mut log = Vec::new();
|
|
||||||
let mut stdout = stdout.lock();
|
|
||||||
let mut tee = TeeWriter::new(&mut stdout, &mut log);
|
|
||||||
|
|
||||||
io::copy(&mut child_stdout, &mut tee).map_err(LuaError::external)?;
|
// TODO: Or maybe we could just spawn four local tasks instead,
|
||||||
|
// one for each vec and one each for Stdout and Stderr, then we
|
||||||
|
// join the local tasks for the vecs to get out our results
|
||||||
|
|
||||||
Ok(log)
|
let stdout_thread = task::spawn_local(async move {
|
||||||
});
|
let mut stdout = io::stdout();
|
||||||
|
let mut log = Vec::new();
|
||||||
|
let mut tee = TeeWriter::new(&mut stdout, &mut log);
|
||||||
|
|
||||||
let stderr_thread = s.spawn(|| {
|
io::copy(&mut child_stdout, &mut tee)
|
||||||
let stderr = io::stderr();
|
.await
|
||||||
let mut log = Vec::new();
|
.map_err(LuaError::external)?;
|
||||||
let mut stderr = stderr.lock();
|
|
||||||
let mut tee = TeeWriter::new(&mut stderr, &mut log);
|
|
||||||
|
|
||||||
io::copy(&mut child_stderr, &mut tee).map_err(LuaError::external)?;
|
Ok(log)
|
||||||
|
});
|
||||||
|
|
||||||
Ok(log)
|
let stderr_thread = task::spawn_local(async move {
|
||||||
});
|
let mut stderr = io::stderr();
|
||||||
|
let mut log = Vec::new();
|
||||||
|
let mut tee = TeeWriter::new(&mut stderr, &mut log);
|
||||||
|
|
||||||
let status = child.wait().expect("child wasn't running");
|
io::copy(&mut child_stderr, &mut tee)
|
||||||
|
.await
|
||||||
|
.map_err(LuaError::external)?;
|
||||||
|
|
||||||
let stdout_log: Result<_, LuaError> = stdout_thread.join().expect("stdout thread panicked");
|
Ok(log)
|
||||||
let stderr_log: Result<_, LuaError> = stderr_thread.join().expect("stderr thread panicked");
|
});
|
||||||
|
|
||||||
Ok::<_, LuaError>((status, stdout_log?, stderr_log?))
|
let status = child.wait().await.expect("child wasn't running");
|
||||||
})
|
|
||||||
|
let stdout_log: Result<_, LuaError> = stdout_thread.await.expect("stdout thread panicked");
|
||||||
|
let stderr_log: Result<_, LuaError> = stderr_thread.await.expect("stderr thread panicked");
|
||||||
|
|
||||||
|
Ok::<_, LuaError>((status, stdout_log?, stderr_log?))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn exit_and_yield_forever(lua: &Lua, exit_code: Option<u8>) -> LuaResult<()> {
|
pub async fn exit_and_yield_forever(lua: &Lua, exit_code: Option<u8>) -> LuaResult<()> {
|
||||||
|
|
Loading…
Reference in a new issue