diff --git a/lib_eio/process.ml b/lib_eio/process.ml index d75e7fc50..79c9a9815 100644 --- a/lib_eio/process.ml +++ b/lib_eio/process.ml @@ -8,12 +8,12 @@ let pp_status ppf = function class virtual t = object (_ : #Generic.t) method probe _ = None method virtual pid : int - method virtual status : status Promise.t + method virtual await_exit : status method virtual stop : unit end let pid proc = proc#pid -let status proc = proc#status +let await_exit proc = proc#await_exit let stop proc = proc#stop class virtual mgr = object (_ : #Generic.t) diff --git a/lib_eio/process.mli b/lib_eio/process.mli index 0bb44f4de..f50aa1a6b 100644 --- a/lib_eio/process.mli +++ b/lib_eio/process.mli @@ -5,14 +5,14 @@ val pp_status : Format.formatter -> status -> unit class virtual t : object inherit Generic.t method virtual pid : int - method virtual status : status Promise.t + method virtual await_exit : status method virtual stop : unit end val pid : #t -> int (** The process ID *) -val status : #t -> status Promise.t +val await_exit : #t -> status (** Checks the status of the subprocess, this will block waiting for the subprocess to terminate or be stopped by a signal. *) diff --git a/lib_eio_linux/eio_linux.ml b/lib_eio_linux/eio_linux.ml index d9796fed5..c1c2a2b48 100644 --- a/lib_eio_linux/eio_linux.ml +++ b/lib_eio_linux/eio_linux.ml @@ -1168,24 +1168,23 @@ let rec wait_for_process flags pid = external sig_to_host_sig : int -> int = "caml_signal_to_posix_signal" -let pid_to_process close pid = object +let pid_to_process p resolver close pid = object inherit Eio.Process.t method pid = pid val mutable needs_close = true method needs_close = needs_close method set_needs_close b = needs_close <- b - method status = - let p, r = Promise.create () in + method await_exit = let run () = let v = wait_for_process [] pid in if needs_close then (close (); needs_close <- false); match v with - | _, WEXITED i -> Promise.resolve r (Eio.Process.Exited (sig_to_host_sig i)) - | _, WSIGNALED i -> Promise.resolve r (Eio.Process.Signaled (sig_to_host_sig i)) - | _, WSTOPPED i -> Promise.resolve r (Eio.Process.Stopped (sig_to_host_sig i)) + | _, WEXITED i -> Promise.resolve resolver (Eio.Process.Exited (sig_to_host_sig i)) + | _, WSIGNALED i -> Promise.resolve resolver (Eio.Process.Signaled (sig_to_host_sig i)) + | _, WSTOPPED i -> Promise.resolve resolver (Eio.Process.Stopped (sig_to_host_sig i)) in Eio_unix.run_in_systhread run; - p + Promise.await p method stop = Unix.kill pid Sys.sigkill @@ -1217,7 +1216,8 @@ let process_mgr = object Unix.close stdout; Unix.close stderr in - let process = pid_to_process close pid in + let status, resolver = Promise.create () in + let process = pid_to_process status resolver close pid in let cleanup () = (* Catch if the process is already finished when trying to stop it. *) (try process#stop with Unix.Unix_error (Unix.ESRCH, _, _) -> ()); @@ -1229,6 +1229,7 @@ let process_mgr = object method spawn_detached ?cwd ~stdin ~stdout ~stderr cmd args = let cwd = Option.map snd cwd in + let status, resolver = Promise.create () in let pid = Option.iter Sys.chdir cwd; Unix.create_process cmd @@ -1237,7 +1238,7 @@ let process_mgr = object (get_fd_or_err stdout) (get_fd_or_err stderr) in - (pid_to_process (fun () -> ()) pid :> Eio.Process.t) + (pid_to_process status resolver (fun () -> ()) pid :> Eio.Process.t) end type stdenv = < diff --git a/lib_eio_luv/eio_luv.ml b/lib_eio_luv/eio_luv.ml index 9a42a45ea..6080aa4fe 100644 --- a/lib_eio_luv/eio_luv.ml +++ b/lib_eio_luv/eio_luv.ml @@ -846,7 +846,7 @@ let process_of_handle status_promise handle = object inherit Eio.Process.t method stop = Luv.Process.kill handle Luv.Signal.sigkill |> or_raise method pid = Luv.Process.pid handle - method status = status_promise + method await_exit = Promise.await status_promise end let get_fd_or_err flow = diff --git a/tests/process.md b/tests/process.md index 209632636..ac1be25b2 100644 --- a/tests/process.md +++ b/tests/process.md @@ -31,7 +31,7 @@ Running a program as a subprocess # run @@ fun spawn env -> Switch.run @@ fun sw -> let t = spawn ~sw "echo" [ "echo"; "hello world" ] in - Promise.await (Process.status t);; + Process.await_exit t;; hello world - : Process.status = Eio.Process.Exited 0 ``` @@ -43,7 +43,7 @@ Stopping a subprocess works and checking the status waits and reports correctly Switch.run @@ fun sw -> let t = spawn ~sw "sleep" [ "sleep"; "10" ] in Process.stop t; - Promise.await (Process.status t);; + Process.await_exit t;; - : Process.status = Eio.Process.Signaled 9 ``` @@ -58,7 +58,7 @@ A switch will stop a process when it is released. proc := Some (spawn ~sw "sleep" [ "sleep"; "10" ]) in run (); - Promise.await @@ Process.status (Option.get !proc);; + Process.await_exit (Option.get !proc);; - : Process.status = Eio.Process.Signaled 9 ``` @@ -74,7 +74,7 @@ Passing in flows allows you to redirect the child process' stdout. let stdout = (stdout :> Eio.Flow.sink) in Switch.run @@ fun sw -> let t = Eio.Process.spawn ~sw ~stdout ~stdin:env#stdin ~stderr:env#stderr process "echo" [ "echo"; "Hello" ] in - Promise.await (Process.status t) + Process.await_exit t in match run () with | Exited 0 -> @@ -109,7 +109,7 @@ val with_pipe_from_child : let t = Eio.Process.spawn ~sw ~stdout:(w :> Flow.sink) ~stdin:env#stdin ~stderr:env#stderr env#process_mgr "echo" [ "echo"; "Hello" ] in - let status = Promise.await (Process.status t) in + let status = Process.await_exit t in Eio.traceln "%a" Eio.Process.pp_status status; Flow.close w; let buff = Buffer.create 10 in @@ -132,17 +132,29 @@ Spawning subprocesses in new domains works normally Eio.Domain_manager.run mgr @@ fun () -> Switch.run @@ fun sw -> let t = spawn ~sw "echo" [ "echo"; "Hello from another domain" ] in - Promise.await (Process.status t);; + Process.await_exit t;; Hello from another domain - : Process.status = Eio.Process.Exited 0 ``` +Calling `await_exit` multiple times on the same spawn just returns the status. + +```ocaml +# run @@ fun spawn env -> + Switch.run @@ fun sw -> + let t = spawn ~sw "echo" [ "echo"; "hello world" ] in + (Process.await_exit t, Process.await_exit t, Process.await_exit t);; +hello world +- : Process.status * Process.status * Process.status = +(Eio.Process.Exited 0, Eio.Process.Exited 0, Eio.Process.Exited 0) +``` + The `spawn_detached` function will spawn a process without needing to provide a switch. ```ocaml # run_detached @@ fun spawn env -> let t = spawn "echo" [ "echo"; "hello world" ] in - Promise.await (Process.status t);; + Process.await_exit t;; hello world - : Process.status = Eio.Process.Exited 0 ```