let () = Miou.run @@ fun () ->
print_endline "Hello World!"
Miou is a library designed to facilitate the development of applications requiring concurrent and/or parallel tasks. This library has been developed with the aim of offering a fairly simple and straightforward design. It's a pretty small library with few dependencies that frames the behaviour of applications using precise and conservative rules to guide users in their development.
The API documentation is available [here][documentation]. It describes (with examples) Miou's behaviour. The official repository is available [here][repository]. We also offer a mirror of this repository on [GitHub][github]. The project is being maintained by the [robur.coop][robur] cooperative.
Miou is focusing on 2 objectives:
Miou meets these objectives by:
A [book][book] is available which explains how to make applications with Miou in details. It introduces the reader to effects, implements a small scheduler and a small echo server as an example. You an also read a simple tutorial from our documentation explaining how to implement this echo server with [here][echo]. Miou is heavily inspired by [picos][picos] and we would like to thanks authors of this project.
Miou complies with several rules that the user must respect. These rules (which can be restrictive) help to guide the user towards good practice and avoid anti-patterns. This notion of rules and anti-patterns is arbitrary 1 - it can therefore be criticised and/or disengage the developer from using Miou. These rules come from our experience of system programming in OCaml, where the development of our software today confirms certain anti-patterns that we would not want to reproduce today (in view of the technical debt that these bring).
There are 2 ways of creating a task:
Miou.async
)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
:
let () = Miou.run @@ fun () ->
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:
let () = Miou.run @@ fun () ->
let p0 = Miou.async @@ fun () -> 42 in
Miou.await_exn p0
A task can only be awaited by the person who created it.
let () = Miou.run @@ fun () ->
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
This rule dictates that passing values from one task to another requires
(pragmatically) that a resource be allocated accordingly to represent such a
transmission. It also reaffirms that such a passage of values must surely be
protected by synchronisation mechanisms between the said tasks (with
Miou.Mutex
or Miou.Condition
).
The only valid relationship (and transmission of values) between 2 tasks offered by Miou is that between a child and its parent.
If a task fails (with an exception), all its sub-tasks also end.
let prgm () = Miou_unix.run @@ fun () ->
let p = Miou.async @@ fun () ->
let q = Miou.async @@ fun () -> Miou_unix.sleep 1. in
raise (Failure "p") in
Miou.await p
let () =
let t0 = Unix.gettimeofday () in
let _ = prgm () in
let t1 = Unix.gettimeofday () in
assert (t1 -. t0 < 1.)
This code shows that if p
fails, we also stop q
(which should wait at least
1 second). This shows that our prgm
didn't actually last a second. Abnormal
termination will always attempt to complete all sub-tasks so that there are no
zombie tasks.
It was explained above that all children must be waited on by the task that
created them. However, the user can also Miou.cancel
a task - of course, this
produces an abnormal termination of the task which automatically results in the
termination of all its children.
let () = Miou.run @@ fun () ->
Miou.cancel (Miou.async @@ fun () -> 42)
This code shows that if it is not possible to ignore
the result of a task, it
is still possible to cancel
it.
Tasks are taken randomly. That is to say that this code could return 1 as 2.
let prgm () =
Miou.run @@ fun () ->
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 =
match prgm () with
| Ok n' when n = n' -> ()
| _ -> untils_its n
let () =
until_its 1;
until_its 2
This code shows that it is possible for our program to return 1 or 2. The reason why we decided to randomly select the promises allows: 1) extend the coverage of your code 2) be less sensitive to predictions that could help an attacker