Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Documentation improvements #389

Merged
merged 2 commits into from
Dec 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
27 changes: 9 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
# Eio -- Effects-Based Parallel IO for OCaml

Eio provides an effects-based direct-style IO stack for OCaml 5.0.
For example, you can use Eio to read and write files, make network connections,
or perform CPU-intensive calculations, running multiple operations at the same time.
It aims to be easy to use, secure, well documented, and fast.
A generic cross-platform API is implemented by optimised backends for different platforms.
Eio replaces existing concurrency libraries such as Lwt
Expand Down Expand Up @@ -78,27 +80,12 @@ and we hope that Eio will be that API.

## Current Status

Platform support:

- Unix and macos: should be fully working using the libuv backend.
- Linux: can additionally use io_uring for better performance on recent kernels.
- Windows: should be mostly working - see [#125](https://github.com/ocaml-multicore/eio/issues/125) for remaining tasks.
- MirageOS: waiting for [ocaml-freestanding](https://github.com/mirage/ocaml-freestanding) to be updated to OCaml 5.0.
- Browsers: waiting for [js_of_ocaml](https://github.com/ocsigen/js_of_ocaml/issues/1088) to be updated to OCaml 5.0.

Feature status:

- Concurrency primitives: Fibers, cancellation, promises, streams and semaphores are all working.
- Multicore support: Working.
- Networking: Clients and servers using TCP, UDP and Unix domain sockets work.
- File-systems: Can create files and directories, load, save, parse, etc. Most other operations missing.
- Spawning sub-processes: Not implemented yet (see [#330](https://github.com/ocaml-multicore/eio/pull/330)).
See [Eio 1.0 progress tracking](https://github.com/ocaml-multicore/eio/issues/388) for the current status.
Please try porting your programs to use Eio and submit PRs or open issues when you find problems.
Remember that you can always fall back to using Lwt libraries to provide missing features if necessary.

See [Awesome Multicore OCaml][] for links to work migrating other projects to Eio.

If you'd like to help out, please try porting your program to use Eio and submit PRs or open issues when you find problems.
Remember that you can always fall back to using Lwt libraries to provide missing features if necessary.

## Structure of the Code

- [Eio][] provides concurrency primitives (promises, etc.) and a high-level, cross-platform OS API.
Expand Down Expand Up @@ -178,6 +165,8 @@ Note that:
- `Eio_main.run` automatically calls the appropriate run function for your platform.
For example, on Linux this will call `Eio_linux.run`. For non-portable code you can use the platform-specific library directly.

This example can also be built using dune; see [examples/hello](./examples/hello/).

## Testing with Mocks

Because external resources are provided to `main` as arguments, we can easily replace them with mocks for testing.
Expand Down Expand Up @@ -516,6 +505,8 @@ let main ~net ~addr =
- : unit = ()
```

See [examples/net](./examples/net/) for a more complete example.

## Design Note: Capabilities

Eio follows the principles of [capability-based security][].
Expand Down
3 changes: 3 additions & 0 deletions examples/hello/dune
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
(executable
(name main)
(libraries eio_main))
6 changes: 6 additions & 0 deletions examples/hello/main.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
let main ~stdout =
Eio.Flow.copy_string "Hello, world!\n" stdout

let () =
Eio_main.run @@ fun env ->
main ~stdout:(Eio.Stdenv.stdout env)
21 changes: 21 additions & 0 deletions examples/net/client.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
open Eio.Std

(* Prefix all trace output with "client: " *)
let traceln fmt = traceln ("client: " ^^ fmt)

module Read = Eio.Buf_read
module Write = Eio.Buf_write

(* Connect to [addr] on [net], send a message and then read the reply. *)
let run ~net ~addr =
traceln "Connecting to server at %a..." Eio.Net.Sockaddr.pp addr;
Switch.run @@ fun sw ->
let flow = Eio.Net.connect ~sw net addr in
(* We use a buffered writer here so we can create the message in multiple
steps but still send it efficiently as a single packet: *)
Write.with_flow flow @@ fun to_server ->
Write.string to_server "Hello";
Write.char to_server ' ';
Write.string to_server "from client\n";
let reply = Read.(parse_exn take_all) flow ~max_size:100 in
traceln "Got reply %S" reply
3 changes: 3 additions & 0 deletions examples/net/dune
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
(executable
(name main)
(libraries eio_main))
20 changes: 20 additions & 0 deletions examples/net/main.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
open Eio.Std

let addr = `Tcp (Eio.Net.Ipaddr.V4.loopback, 8080)

(* Run a server and a test client, communicating using [net]. *)
let main ~net =
Switch.run @@ fun sw ->
(* We create the listening socket first so that we can be sure it is ready
as soon as the client wants to use it. *)
let listening_socket = Eio.Net.listen net ~sw ~reuse_addr:true ~backlog:5 addr in
(* Start the server running in a new fiber.
Using [fork_daemon] here means that it will be stopped once the client is done
(we don't wait for it to finish because it will keep accepting new connections forever). *)
Fiber.fork_daemon ~sw (fun () -> Server.run listening_socket);
(* Test the server: *)
Client.run ~net ~addr

let () =
Eio_main.run @@ fun env ->
main ~net:(Eio.Stdenv.net env)
30 changes: 30 additions & 0 deletions examples/net/server.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
open Eio.Std

(* Prefix all trace output with "server: " *)
let traceln fmt = traceln ("server: " ^^ fmt)

module Read = Eio.Buf_read

(* Read one line from [client] and respond with "OK". *)
let handle_client flow addr =
traceln "Accepted connection from %a" Eio.Net.Sockaddr.pp addr;
(* We use a buffered reader because we may need to combine multiple reads
to get a single line (or we may get multiple lines in a single read,
although here we only use the first one). *)
let from_client = Read.of_flow flow ~max_size:100 in
traceln "Received: %S" (Read.line from_client);
Eio.Flow.copy_string "OK" flow

(* Accept incoming client connections on [socket].
We can handle multiple clients at the same time.
Never returns (but can be cancelled). *)
let run socket =
Switch.run @@ fun sw ->
let rec serve () =
Eio.Net.accept_fork ~sw socket handle_client
~on_error:(traceln "Error handling connection: %a" Fmt.exn);
(* Loop to accept more connections (while the new fiber handles the
one we just got): *)
serve ()
in
serve ()