Skip to content

Commit

Permalink
Rename exit_status to await and await_exn and add docs
Browse files Browse the repository at this point in the history
  • Loading branch information
patricoferris committed Apr 1, 2023
1 parent f402423 commit 5afdbd4
Show file tree
Hide file tree
Showing 9 changed files with 87 additions and 23 deletions.
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Eio replaces existing concurrency libraries such as Lwt
* [Buffered Writing](#buffered-writing)
* [Error Handling](#error-handling)
* [Filesystem Access](#filesystem-access)
* [Subprocesses](#subprocesses)
* [Time](#time)
* [Multicore Support](#multicore-support)
* [Synchronisation Tools](#synchronisation-tools)
Expand Down Expand Up @@ -891,6 +892,36 @@ Note: the `eio_luv` backend doesn't have the `openat`, `mkdirat`, etc.,
calls that are necessary to implement these checks without races,
so be careful if symlinks out of the subtree may be created while the program is running.

## Subprocesses

Spawning subprocesses is provided by the [Eio.Process][] module.

```ocaml
# Eio_main.run @@ fun env ->
let proc_mgr = Eio.Stdenv.process_mgr env in
let stdin, stdout, stderr = Eio.Stdenv.stdio env in
Eio.Switch.run @@ fun sw ->
let child = Eio.Process.spawn proc_mgr ~sw ~stdin ~stdout ~stderr "/usr/bin/echo" [ "echo"; "hello" ] in
Eio.Process.await child;;
hello
- : Eio.Process.status = Eio.Process.Exited 0
```

If you want to capture the output of a process, you can provide a suitable `Eio.Flow.sink` as the `stdout` argument.

```ocaml
# Eio_main.run @@ fun env ->
let proc_mgr = Eio.Stdenv.process_mgr env in
let buffer = Buffer.create 4 in
let stdin, _, stderr = Eio.Stdenv.stdio env in
let stdout = Eio.Flow.buffer_sink buffer in
Eio.Switch.run @@ fun sw ->
let child = Eio.Process.spawn proc_mgr ~sw ~stdin ~stdout ~stderr "/usr/bin/echo" [ "echo"; "hello" ] in
Eio.Process.await_exn child;
Buffer.contents buffer;;
- : string = "hello\n"
```

## Time

The standard environment provides a [clock][Eio.Time] with the usual POSIX time:
Expand Down Expand Up @@ -1743,3 +1774,4 @@ Some background about the effects system can be found in:
[Eio.Mutex]: https://ocaml-multicore.github.io/eio/eio/Eio/Mutex/index.html
[Eio.Semaphore]: https://ocaml-multicore.github.io/eio/eio/Eio/Semaphore/index.html
[Eio.Condition]: https://ocaml-multicore.github.io/eio/eio/Eio/Condition/index.html
[Eio.Process]: https://ocaml-multicore.github.io/eio/eio/Eio/Process/index.html
14 changes: 8 additions & 6 deletions doc/prelude.ml
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,14 @@ module Eio_main = struct
let run fn =
Eio_main.run @@ fun env ->
fn @@ object
method net = env#net
method stdin = env#stdin
method stdout = env#stdout
method cwd = env#cwd
method domain_mgr = fake_domain_mgr
method clock = fake_clock env#clock
method net = env#net
method stdin = env#stdin
method stdout = env#stdout
method stderr = env#stderr
method cwd = env#cwd
method process_mgr = env#process_mgr
method domain_mgr = fake_domain_mgr
method clock = fake_clock env#clock
end
end

Expand Down
1 change: 1 addition & 0 deletions lib_eio/eio.ml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ module Stdenv = struct
let stdin (t : <stdin : #Flow.source; ..>) = t#stdin
let stdout (t : <stdout : #Flow.sink; ..>) = t#stdout
let stderr (t : <stderr : #Flow.sink; ..>) = t#stderr
let stdio (t : <stdin : #Flow.source; stdout: #Flow.sink; stderr : #Flow.sink; ..>) = t#stdin, t#stdout, t#stderr
let net (t : <net : #Net.t; ..>) = t#net
let process_mgr (t : <process_mgr : #Process.mgr; ..>) = t#process_mgr
let domain_mgr (t : <domain_mgr : #Domain_manager.t; ..>) = t#domain_mgr
Expand Down
3 changes: 3 additions & 0 deletions lib_eio/eio.mli
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,9 @@ module Stdenv : sig
val stdout : <stdout : #Flow.sink as 'a; ..> -> 'a
val stderr : <stderr : #Flow.sink as 'a; ..> -> 'a

val stdio : <stdin : #Flow.source as 'a; stdout : #Flow.sink as 'b; stderr : #Flow.sink as 'c; ..> -> 'a * 'b * 'c
(** [stdio t] returns [stdin, stdout, stderr]. *)

(** {1 File-system access}
To use these, see {!Path}. *)
Expand Down
23 changes: 21 additions & 2 deletions lib_eio/process.ml
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,33 @@ let pp_status ppf = function
| Signaled i -> Format.fprintf ppf "Signalled %i" i
| Stopped i -> Format.fprintf ppf "Stopped %i" i

type Exn.err += E of status

let err e = Exn.create (E e)

let () =
Exn.register_pp (fun f -> function
| E e ->
Fmt.string f "Process ";
pp_status f e;
true
| _ -> false
)

class virtual t = object
method virtual pid : int
method virtual exit_status : status
method virtual await : status
method virtual signal : int -> unit
end

let pid proc = proc#pid
let exit_status proc = proc#exit_status
let await proc = proc#await

let await_exn proc =
match proc#await with
| Exited 0 -> ()
| status -> raise (err status)

let signal proc = proc#signal

class virtual mgr = object
Expand Down
15 changes: 11 additions & 4 deletions lib_eio/process.mli
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,25 @@ type status = Exited of int | Signaled of int | Stopped of int

val pp_status : Format.formatter -> status -> unit

type Exn.err += E of status

val err : status -> exn
(** [err e] is [Eio.Exn.create (E e)] *)

class virtual t : object
method virtual pid : int
method virtual exit_status : status
method virtual await : status
method virtual signal : int -> unit
end

val pid : #t -> int
(** The process ID *)

val exit_status : #t -> status
(** Reports the exit status of the subprocess. This will block waiting for the subprocess
to exit. *)
val await : #t -> status
(** This functions waits for the subprocess to exit and then reports the status. *)

val await_exn : #t -> unit
(** Like {! await} except an exception is raised if the status is not [Exited 0]. *)

val signal : #t -> int -> unit
(** [signal t i] sends the signal [i] to process [t]. *)
Expand Down
2 changes: 1 addition & 1 deletion lib_eio_linux/eio_linux.ml
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ let unix_status_to_stats = function

let process proc : Eio.Process.t = object
method pid = Process.pid proc
method exit_status =
method await =
let status = Eio.Promise.await @@ Process.exit_status proc in
unix_status_to_stats status
method signal i = Process.signal proc i
Expand Down
2 changes: 1 addition & 1 deletion lib_eio_posix/process.ml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ let unix_status_to_stats = function

let process proc : Eio.Process.t = object
method pid = Process.pid proc
method exit_status =
method await =
let status = Eio.Promise.await @@ Process.exit_status proc in
unix_status_to_stats status
method signal i = Process.signal proc i
Expand Down
18 changes: 9 additions & 9 deletions tests/process.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Running a program as a subprocess
# run @@ fun spawn env ->
Switch.run @@ fun sw ->
let t = spawn ~sw "/usr/bin/echo" [ "echo"; "hello world" ] in
Process.exit_status t;;
Process.await t;;
hello world
- : Process.status = Eio.Process.Exited 0
```
Expand All @@ -36,7 +36,7 @@ Stopping a subprocess works and checking the status waits and reports correctly
Switch.run @@ fun sw ->
let t = spawn ~sw "/usr/bin/sleep" [ "sleep"; "10" ] in
Process.signal t Sys.sigkill;
Process.exit_status t;;
Process.await t;;
- : Process.status = Eio.Process.Signaled (-7)
```

Expand All @@ -51,7 +51,7 @@ A switch will stop a process when it is released.
proc := Some (spawn ~sw "/usr/bin/sleep" [ "sleep"; "10" ])
in
run ();
Process.exit_status (Option.get !proc);;
Process.await (Option.get !proc);;
- : Process.status = Eio.Process.Signaled (-7)
```

Expand All @@ -67,7 +67,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 "/usr/bin/echo" [ "echo"; "Hello" ] in
Process.exit_status t
Process.await t
in
match run () with
| Exited 0 -> Eio.Path.(load (fs / filename))
Expand Down Expand Up @@ -98,7 +98,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 "/usr/bin/echo" [ "echo"; "Hello" ]
in
let status = Process.exit_status t in
let status = Process.await t in
Eio.traceln "%a" Eio.Process.pp_status status;
Flow.close w;
let buff = Buffer.create 10 in
Expand All @@ -122,7 +122,7 @@ Spawning subprocesses in new domains works normally
Eio.Domain_manager.run mgr @@ fun () ->
Switch.run @@ fun sw ->
let t = spawn ~sw "/usr/bin/echo" [ "echo"; "Hello from another domain" ] in
Process.exit_status t;;
Process.await t;;
Hello from another domain
- : Process.status = Eio.Process.Exited 0
```
Expand All @@ -133,7 +133,7 @@ Calling `await_exit` multiple times on the same spawn just returns the status.
# run @@ fun spawn env ->
Switch.run @@ fun sw ->
let t = spawn ~sw "/usr/bin/echo" [ "echo"; "hello world" ] in
(Process.exit_status t, Process.exit_status t, Process.exit_status t);;
(Process.await t, Process.await t, Process.await t);;
hello world
- : Process.status * Process.status * Process.status =
(Eio.Process.Exited 0, Eio.Process.Exited 0, Eio.Process.Exited 0)
Expand All @@ -150,7 +150,7 @@ Using sources and sinks that are not backed by file descriptors.
let p =
Eio.Process.spawn proc ~sw ~stdin:env#stdin ~stdout:dst ~stderr:env#stderr "/usr/bin/echo" [ "echo"; "Hello, world" ]
in
let _ : Process.status = Process.exit_status p in
let _ : Process.status = Process.await p in
Buffer.contents buf
- : string = "Hello, world\n"
```
Expand All @@ -162,7 +162,7 @@ Changing directory
Switch.run @@ fun sw ->
let root = Eio.Path.(env#fs / "/") in
let child = spawn ~cwd:root ~sw "/usr/bin/env" [ "env"; "pwd" ] in
Process.exit_status child
Process.await child
/
- : Process.status = Eio.Process.Exited 0
```

0 comments on commit 5afdbd4

Please sign in to comment.