diff --git a/crates/lune-std-process/src/lib.rs b/crates/lune-std-process/src/lib.rs index 4212a77..a83f59d 100644 --- a/crates/lune-std-process/src/lib.rs +++ b/crates/lune-std-process/src/lib.rs @@ -79,7 +79,7 @@ pub fn module(lua: &Lua) -> LuaResult { .with_value("env", env_tab)? .with_value("exit", process_exit)? .with_async_function("exec", process_exec)? - .with_async_function("create", process_spawn)? + .with_function("create", process_spawn)? .build_readonly() } @@ -153,7 +153,7 @@ async fn process_exec( ) -> LuaResult { let res = lua .spawn(async move { - let cmd = spawn_command(program, args, options.clone()).await?; + let cmd = spawn_command_with_stdin(program, args, options.clone()).await?; wait_for_child(cmd, options.stdio.stdout, options.stdio.stderr).await }) .await?; @@ -180,7 +180,7 @@ async fn process_exec( } #[allow(clippy::await_holding_refcell_ref)] -async fn process_spawn( +fn process_spawn( lua: &Lua, (program, args, options): (String, Option>, ProcessSpawnOptions), ) -> LuaResult { @@ -189,26 +189,15 @@ async fn process_spawn( let mut spawn_options = options.clone(); spawn_options.stdio = ProcessSpawnOptionsStdio::default(); - let (stdin_tx, stdin_rx) = tokio::sync::oneshot::channel(); - let (stdout_tx, stdout_rx) = tokio::sync::oneshot::channel(); - let (stderr_tx, stderr_rx) = tokio::sync::oneshot::channel(); let (code_tx, code_rx) = tokio::sync::broadcast::channel(4); let code_rx_rc = Rc::new(RefCell::new(code_rx)); - tokio::spawn(async move { - let mut child = spawn_command(program, args, spawn_options) - .await - .expect("Could not spawn child process"); - stdin_tx - .send(child.stdin.take()) - .expect("Stdin receiver was unexpectedly dropped"); - stdout_tx - .send(child.stdout.take()) - .expect("Stdout receiver was unexpectedly dropped"); - stderr_tx - .send(child.stderr.take()) - .expect("Stderr receiver was unexpectedly dropped"); + let mut child = spawn_command(program, args, spawn_options)?; + let stdin = child.stdin.take().unwrap(); + let stdout = child.stdout.take().unwrap(); + let stderr = child.stderr.take().unwrap(); + tokio::spawn(async move { let res = child .wait_with_output() .await @@ -225,33 +214,9 @@ async fn process_spawn( }); TableBuilder::new(lua)? - .with_value( - "stdout", - ChildProcessReader( - stdout_rx - .await - .expect("Stdout sender unexpectedly dropped") - .unwrap(), - ), - )? - .with_value( - "stderr", - ChildProcessReader( - stderr_rx - .await - .expect("Stderr sender unexpectedly dropped") - .unwrap(), - ), - )? - .with_value( - "stdin", - ChildProcessWriter( - stdin_rx - .await - .expect("Stdin sender unexpectedly dropped") - .unwrap(), - ), - )? + .with_value("stdout", ChildProcessReader(stdout))? + .with_value("stderr", ChildProcessReader(stderr))? + .with_value("stdin", ChildProcessWriter(stdin))? .with_async_function("status", move |lua, ()| { let code_rx_rc_clone = Rc::clone(&code_rx_rc); async move { @@ -270,21 +235,14 @@ async fn process_spawn( .build_readonly() } -async fn spawn_command( +async fn spawn_command_with_stdin( program: String, args: Option>, mut options: ProcessSpawnOptions, ) -> LuaResult { - let stdout = options.stdio.stdout; - let stderr = options.stdio.stderr; let stdin = options.stdio.stdin.take(); - let mut child = options - .into_command(program, args) - .stdin(Stdio::piped()) - .stdout(stdout.as_stdio()) - .stderr(stderr.as_stdio()) - .spawn()?; + let mut child = spawn_command(program, args, options)?; if let Some(stdin) = stdin { let mut child_stdin = child.stdin.take().unwrap(); @@ -293,3 +251,21 @@ async fn spawn_command( Ok(child) } + +fn spawn_command( + program: String, + args: Option>, + options: ProcessSpawnOptions, +) -> LuaResult { + let stdout = options.stdio.stdout; + let stderr = options.stdio.stderr; + + let child = options + .into_command(program, args) + .stdin(Stdio::piped()) + .stdout(stdout.as_stdio()) + .stderr(stderr.as_stdio()) + .spawn()?; + + Ok(child) +} diff --git a/tests/process/spawn/non_blocking.luau b/tests/process/spawn/non_blocking.luau index d44a9d2..82352a7 100644 --- a/tests/process/spawn/non_blocking.luau +++ b/tests/process/spawn/non_blocking.luau @@ -1,18 +1,13 @@ local process = require("@lune/process") --- Spawning a child process should not block the main thread +-- Spawning a child process should not block the thread -local SAMPLES = 400 +local childThread = coroutine.create(process.create) -for _ = 1, SAMPLES do - local start = os.time() - local child = process.create("echo", { "hello, world" }) +local ok, err = coroutine.resume(childThread, "echo", { "hello, world" }) +assert(ok, err) - assert(child ~= nil, "Failed to spawn child process") - - local delta = os.time() - start - assert( - delta <= 1, - `Spawning a child process should not block the main thread, process.spawn took {delta}s to return when it should return immediately` - ) -end +assert( + coroutine.status(childThread) == "dead", + "Child process should not block the thread it is running on" +) diff --git a/tests/process/spawn/stream.luau b/tests/process/spawn/stream.luau index 965eab1..89bb61a 100644 --- a/tests/process/spawn/stream.luau +++ b/tests/process/spawn/stream.luau @@ -15,7 +15,4 @@ local echoChild = if process.os == "windows" then process.create("/c", { "echo", msg, "1>&2" }, { shell = "cmd" }) else process.create("echo", { msg, ">>/dev/stderr" }, { shell = true }) -assert( - msg == echoChild.stderr:read(#msg), - "Failed to read from stderr of child process" -) +assert(msg == echoChild.stderr:read(#msg), "Failed to read from stderr of child process")