Skip to content

Commit

Permalink
Merge pull request #23 from robur-coop/async
Browse files Browse the repository at this point in the history
Rename `Miou.call_cc` to `Miou.async`
  • Loading branch information
dinosaure authored Jun 4, 2024
2 parents b43d607 + 1638a3d commit 5b038c3
Show file tree
Hide file tree
Showing 17 changed files with 130 additions and 130 deletions.
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,23 +48,23 @@ technical debt that these bring).

There are 2 ways of creating a task:
- it can run concurrently with other tasks and execute on the domain in which it
was created (see `Miou.call_cc`)
was created (see `Miou.async`)
- it can run in parallel with other tasks and be executed on **another** domain
(see `Miou.call`)

The first rule to follow is that the user must wait for all the tasks he/she has
created. If they don't, Miou raises an exception: `Still_has_children`:
```ocaml
let () = Miou.run @@ fun () ->
ignore (Miou.call_cc @@ fun () -> 42)
ignore (Miou.async @@ fun () -> 42)
Exception: Miou.Still_has_children
```

The user must therefore take care to use `Miou.await` for all the tasks
(concurrent and parallel) that he/she has created:
```ocaml
let () = Miou.run @@ fun () ->
let p0 = Miou.call_cc @@ fun () -> 42 in
let p0 = Miou.async @@ fun () -> 42 in
Miou.await_exn p0
```

Expand All @@ -73,8 +73,8 @@ let () = Miou.run @@ fun () ->
A task can only be awaited by the person who created it.
```ocaml
let () = Miou.run @@ fun () ->
let p0 = Miou.call_cc @@ fun () -> 42 in
let p1 = Miou.call_cc @@ fun () -> Miou.await_exn p0 in
let p0 = Miou.async @@ fun () -> 42 in
let p1 = Miou.async @@ fun () -> Miou.await_exn p0 in
Miou.await_exn p1
Esxception: Miou.Not_a_child
```
Expand All @@ -94,8 +94,8 @@ If a task fails (with an exception), all its sub-tasks also end.

```ocaml
let prgm () = Miouu.run @@ fun () ->
let p = Miou.call_cc @@ fun () ->
let q = Miou.call_cc @@ fun () -> sleep 1. in
let p = Miou.async @@ fun () ->
let q = Miou.async @@ fun () -> sleep 1. in
raise (Failure "p") in
Miou.await p
Expand All @@ -120,7 +120,7 @@ termination of all its children.

```ocaml
let () = Miou.run @@ fun () ->
Miou.cancel (Miou.call_cc @@ fun () -> 42)
Miou.cancel (Miou.async @@ fun () -> 42)
```

This code shows that if it is not possible to `ignore` the result of a task, it
Expand All @@ -132,8 +132,8 @@ Tasks are taken randomly. That is to say that this code could return 1 as 2.
```ocaml
let prgm () =
Miou.run @@ fun () ->
let a = Miou.call_cc (Fun.const 1) in
let b = Miou.call_cc (Fun.const 2) in
let a = Miou.async (Fun.const 1) in
let b = Miou.async (Fun.const 2) in
Miou.await_first [ a; b ]
let rec until_its n =
Expand Down
16 changes: 8 additions & 8 deletions book/src/conditions_and_mutexes.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ In all other cases (for example, between two tasks with no direct parent-child
relationship and executing in parallel), we need to consider how to transfer
this information correctly (meaning that this transfer would work regardless of
the execution order of our tasks from both Miou's perspective — for
`Miou.call_cc` — and the system's perspective — for `Miou.call`). It is in these
`Miou.async` — and the system's perspective — for `Miou.call`). It is in these
cases that Mutexes and Conditions can be useful.

## Mutexes
Expand All @@ -46,7 +46,7 @@ let server () =
clean_up orphans;
let client, sockaddr = Miou_unix.Ownership.accept socket in
Format.printf "new client: %a\n%!" pp_sockaddr sockaddr;
ignore (Miou.call_cc
ignore (Miou.async
~give:[ Miou_unix.Ownership.resource clientr
~orphans (fun () -> echo client))
done;
Expand Down Expand Up @@ -106,7 +106,7 @@ let accept_or_die fd =
Miou.Mutex.protect mutex_sigint @@ fun () ->
Miou.Condition.wait condition mutex_sigint;
`Die in
Miou.await_first [ Miou.call_cc accept; Miou.call_cc or_die ]
Miou.await_first [ Miou.async accept; Miou.async or_die ]
|> function Ok value -> value | Error exn -> raise exn
let server () =
Expand All @@ -119,7 +119,7 @@ let server () =
| `Die -> ()
| `Accept (fd', sockaddr) ->
printf "new client: %a\n%!" pp_sockaddr sockaddr;
ignore (Miou.call_cc
ignore (Miou.async
~give:[ Miou_unix.Ownership.resource client ]
~orphans (fun () -> echo client));
go orphans in
Expand All @@ -139,7 +139,7 @@ let () = Miou_unix.run @@ fun () ->
ignore (Miou.sys_signal Sys.sigint (Sys.Signal_handle stop));
let domains = Stdlib.Domain.recommended_domain_count () - 1 in
let domains = List.init domains (Fun.const ()) in
let prm = Miou.call_cc server in
let prm = Miou.async server in
Miou.await prm :: Miou.parallel server domains
|> List.iter @@ function
| Ok () -> ()
Expand Down Expand Up @@ -223,7 +223,7 @@ let accept_or_die fd =
Miou.Condition.wait condition mutex_sigint;
`Die in
let give = [ Miou_unix.Ownership.resource fd ] in
Miou.await_first [ Miou.call_cc ~give accept; Miou.call_cc or_die ]
Miou.await_first [ Miou.async ~give accept; Miou.async or_die ]
|> function Ok value -> value | Error exn -> raise exn
let pp_sockaddr ppf = function
Expand Down Expand Up @@ -256,7 +256,7 @@ let server () =
| `Die -> terminate orphans
| `Accept (client, sockaddr) ->
printf "new client: %a\n%!" pp_sockaddr sockaddr;
ignore (Miou.call_cc
ignore (Miou.async
~give:[ Miou_unix.Ownership.resource client ]
~orphans (fun () -> echo client));
go orphans in
Expand All @@ -270,7 +270,7 @@ let () = Miou_unix.run @@ fun () ->
ignore (Miou.sys_signal Sys.sigint (Sys.Signal_handle stop));
let domains = Stdlib.Domain.recommended_domain_count () - 1 in
let domains = List.init domains (Fun.const ()) in
let prm = Miou.call_cc server in
let prm = Miou.async server in
Miou.await prm :: Miou.parallel server domains
|> List.iter @@ function
| Ok () -> ()
Expand Down
8 changes: 4 additions & 4 deletions book/src/echo.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ let server () =
while true do
clean_up orphans;
let client, _ = Miou_unix.accept socket in
ignore (Miou.call_cc ~orphans (fun () -> echo client))
ignore (Miou.async ~orphans (fun () -> echo client))
done;
Miou_unix.close socket
Expand Down Expand Up @@ -78,12 +78,12 @@ servers in parallel, each handling several clients concurrently.
To distribute the implementation of our server across multiple domains, we'll
use `Miou.parallel`. We won't forget to involve `dom0` (referring to our rule
where `dom0` would never be assigned a task from other domain) via
`Miou.call_cc`:
`Miou.async`:
```ocaml
let () = Miou_unix.run @@ fun () ->
let domains = Stdlib.Domain.recommended_domain_count () - 1 in
let domains = List.init domains (Fun.const ()) in
let prm = Miou.call_cc server in
let prm = Miou.async server in
Miou.await prm :: Miou.parallel server domains
|> List.iter @@ function
| Ok () -> ()
Expand Down Expand Up @@ -176,7 +176,7 @@ let server () =
while true do
clean_up orphans;
let client, _ = Miou_unix.Ownership.accept socket in
ignore (Miou.call_cc
ignore (Miou.async
~give:[ Miou_unix.Ownership.resource client ]
~orphans (fun () -> echo client))
done;
Expand Down
30 changes: 15 additions & 15 deletions book/src/retrospective.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,21 @@ client management in the background:
ignore (spawn @@ fun () -> echo client)
```

You can achieve the same thing with `Miou.call_cc`. This function essentially
You can achieve the same thing with `Miou.async`. This function essentially
does will more what our `spawn` does: it creates a new task that will run on the
same _thread_ using our scheduler. This type of task which coexists with others
on the same thread is called a _[fiber][fiber]_. And, just like `spawn`,
`Miou.call_cc` also returns a promise for this task. In Miou, you're creating a
`Miou.async` also returns a promise for this task. In Miou, you're creating a
child of your task, a subtask.

The key difference with Miou, though, is that you can't forget your children!
```ocaml
let () = Miou.run @@ fun () ->
ignore (Miou.call_cc @@ fun () -> print_endline "Hello World!")
ignore (Miou.async @@ fun () -> print_endline "Hello World!")
Exception: Miou.Still_has_children.
```

In Miou, we treat a task as a resource. You allocate it (using `Miou.call_cc`),
In Miou, we treat a task as a resource. You allocate it (using `Miou.async`),
but you also have to release it with `Miou.await`. A fundamental rule governs
Miou programs: all tasks must either be awaited or canceled. Forgetting a task
will result in a fatal error.
Expand Down Expand Up @@ -58,7 +58,7 @@ let server () =
while true do
clean_up orphans;
let client, address_of_client = Miou_unix.accept socket in
ignore (Miou.call_cc ~orphans @@ fun () -> echo client)
ignore (Miou.async ~orphans @@ fun () -> echo client)
done
```

Expand Down Expand Up @@ -89,8 +89,8 @@ There's a second rule: only the **direct** parent can wait for or cancel its
children. For instance, the following code is incorrect:
```ocaml
let () = Miou.run @@ fun () ->
let a = Miou.call_cc @@ fun () -> Miou.yield () in
let b = Miou.call_cc @@ fun () -> Miou.await_exn a in
let a = Miou.async @@ fun () -> Miou.yield () in
let b = Miou.async @@ fun () -> Miou.await_exn a in
Miou.await_exn a;
Miou.await_exn b
Exception: Miou.Not_a_child.
Expand All @@ -108,16 +108,16 @@ cancellation. It can be useful to cancel a task that, for example, is taking too
long. Miou provides this mechanism for all tasks using their promises.
```ocaml
let () = Miou.run @@ fun () ->
let prm = Miou.call_cc @@ fun () -> print_endline "Hello World" in
let prm = Miou.async @@ fun () -> print_endline "Hello World" in
Miou.cancel prm
```

The rules of parentage explained earlier also apply to cancellation. You can
only cancel your direct children:
```ocaml
let () = Miou.run @@ fun () ->
let a = Miou.call_cc @@ fun () -> Miou.yield () in
let b = Miou.call_cc @@ fun () -> Miou.cancel a in
let a = Miou.async @@ fun () -> Miou.yield () in
let b = Miou.async @@ fun () -> Miou.cancel a in
Miou.await_exn a;
Miou.await_exn b
Exception: Miou.Not_a_child.
Expand All @@ -143,7 +143,7 @@ canceling all its sub-tasks:
let rec infinite () = infinite (Miou.yield ())
let () = Miou.run @@ fun () ->
let p = Miou.call_cc @@ fun () ->
let p = Miou.async @@ fun () ->
let q = Miou.call infinite in
Miou.await_exn q in
Miou.cancel p
Expand All @@ -158,7 +158,7 @@ runs despite the cancellation of `p1`:
let () = Miou.run @@ fun () ->
let p1 = Miou.call @@ fun () -> Miou.yield () in
let v = ref false in
let p0 = Miou.call_cc @@ fun () -> print_endline "Do p0" in
let p0 = Miou.async @@ fun () -> print_endline "Do p0" in
print_endline "Cancel p1";
Miou.cancel p1;
print_endline "p1 cancelled";
Expand Down Expand Up @@ -193,7 +193,7 @@ let () = Miou.run @@ fun () ->

Miou takes care of allocating multiple domains according to your system's
specifics. These domains will be waiting for tasks, and `Miou.call` notifies
them of a new task to perform. Just like `Miou.call_cc`, `Miou.call` also
them of a new task to perform. Just like `Miou.async`, `Miou.call` also
returns a promise, and the same rules apply: you must not forget about your
children.

Expand Down Expand Up @@ -251,13 +251,13 @@ This rule prevents a domain from waiting for another domain, which waits for
another domain, which waits for `dom0`, which waits for your first domain - the
[starvation problem][starvation]. Thus, it may happen that `dom0` is no longer
involved in the execution of your program and is only waiting for the other
domains. However, we can involve it using `Miou.call_cc`:
domains. However, we can involve it using `Miou.async`:

```ocaml
let task () : int = (Stdlib.Domain.self () :> int)
let () = Miou.run ~domains:3 @@ fun () ->
let prm = Miou.call_cc my_super_task in
let prm = Miou.async my_super_task in
let domains =
Miou.await prm
:: Miou.parallel task (List.init 3 (Fun.const ())) in
Expand Down
6 changes: 3 additions & 3 deletions book/src/sleepers.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ the one we need to implement.
```ocaml
let program () =
Chat.run @@ fun () ->
let a = Miou.call_cc @@ fun () -> Chat.sleep 1. in
let b = Miou.call_cc @@ fun () -> Chat.sleep 2. in
let a = Miou.async @@ fun () -> Chat.sleep 1. in
let b = Miou.async @@ fun () -> Chat.sleep 2. in
Miou.await_all [ a; b ]
|> List.iter @@ function
| Ok () -> ()
Expand Down Expand Up @@ -201,7 +201,7 @@ let select ~block:_ _ =
remove_and_signal_older sleepers []
```

We can now safely replace our `Miou.call_cc` with `Miou.call`. We know that each
We can now safely replace our `Miou.async` with `Miou.call`. We know that each
task will have its own `sleepers`, and there will be no illegal access between
domains.

Expand Down
6 changes: 3 additions & 3 deletions lib/miou.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1034,7 +1034,7 @@ module Ownership = struct
| exception _ -> Miou_sequence.remove node)
end

let call_cc ?(give = []) ?orphans fn =
let async ?(give = []) ?orphans fn =
let prm = Effect.perform (Spawn (Concurrent, false, give, fn)) in
Logs.debug (fun m -> m "%a spawned" Promise.pp prm);
Option.iter (fun s -> Miou_sequence.(add Left) s prm) orphans;
Expand Down Expand Up @@ -1123,7 +1123,7 @@ let await_one prms =
in
try_attach_all [] prms
in
let prm = call_cc choose in
let prm = async choose in
miou_assert (await_exn prm);
match Computation.await_exn c with
| Ok value -> Ok value
Expand Down Expand Up @@ -1198,7 +1198,7 @@ let await_first prms =
in
try_attach_all [] prms
in
let prm = call_cc choose in
let prm = async choose in
miou_assert (await_exn prm);
match Computation.await_exn c with
| Ok value -> Ok value
Expand Down
Loading

0 comments on commit 5b038c3

Please sign in to comment.