test: update readme and the mdx test

This commit is contained in:
Simon Cruanes 2025-10-22 12:00:05 -04:00
parent 4924b5f52b
commit 95de0e7e27
No known key found for this signature in database
GPG key ID: EBFFF6F283F3A2B4
4 changed files with 10 additions and 52 deletions

View file

@ -173,49 +173,9 @@ val expected_sum : int = 5050
We have a `Exn_bt.t` type that comes in handy in many places. It bundles together We have a `Exn_bt.t` type that comes in handy in many places. It bundles together
an exception and the backtrace associated with the place the exception was caught. an exception and the backtrace associated with the place the exception was caught.
### Fibers ### Local storage
On OCaml 5, Moonpool comes with a library `moonpool.fib` (module `Moonpool_fib`) Moonpool, via picos, provides _task local storage_ (like thread-local storage, but per task).
which provides _lightweight fibers_
that can run on any Moonpool runner.
These fibers are a sort of lightweight thread, dispatched on the runner's
background thread(s).
Fibers rely on effects to implement `Fiber.await`, suspending themselves until the `await`-ed fiber
is done.
```ocaml
# #require "moonpool.fib";;
...
# (* convenient alias *)
module F = Moonpool_fib;;
module F = Moonpool_fib
# F.main (fun _runner ->
let f1 = F.spawn (fun () -> fib 10) in
let f2 = F.spawn (fun () -> fib 15) in
F.await f1 + F.await f2);;
- : int = 1076
```
Fibers form a _tree_, where a fiber calling `Fiber.spawn` to start a sub-fiber is
the sub-fiber's _parent_.
When a parent fails, all its children are cancelled (forced to fail).
This is a simple form of [Structured Concurrency](https://en.wikipedia.org/wiki/Structured_concurrency).
Like a future, a fiber eventually _resolves_ into a value (or an `Exn_bt.t`) that it's possible
to `await`. With `Fiber.res : 'a Fiber.t -> 'a Fut.t` it's possible to access that result
as a regular future, too.
However, this resolution is only done after all the children of the fiber have
resolved — the lifetime of fibers forms a well-nested tree in that sense.
When a fiber is suspended because it `await`s another fiber (or future), the scheduler's
thread on which it was running becomes available again and can go on process another task.
When the fiber resumes, it will automatically be re-scheduled on the same runner it started on.
This means fibers on pool P1 can await fibers from pool P2 and still be resumed on P1.
In addition to all that, fibers provide _fiber local storage_ (like thread-local storage, but per fiber).
This storage is inherited in `spawn` (as a shallow copy only — it's advisable to only
put persistent data in storage to avoid confusing aliasing).
The storage is convenient for carrying around context for cross-cutting concerns such The storage is convenient for carrying around context for cross-cutting concerns such
as logging or tracing (e.g. a log tag for the current user or request ID, or a tracing as logging or tracing (e.g. a log tag for the current user or request ID, or a tracing
scope). scope).

2
dune
View file

@ -3,7 +3,7 @@
(flags :standard -strict-sequence -warn-error -a+8 -w +a-4-40-42-70))) (flags :standard -strict-sequence -warn-error -a+8 -w +a-4-40-42-70)))
(mdx (mdx
(libraries moonpool moonpool.forkjoin moonpool.fib threads) (libraries moonpool moonpool.forkjoin threads)
(package moonpool) (package moonpool)
(enabled_if (enabled_if
(>= %{ocaml_version} 5.0))) (>= %{ocaml_version} 5.0)))

View file

@ -1,11 +1,12 @@
(** Example from (** NOTE: this was an example from
https://discuss.ocaml.org/t/confused-about-moonpool-cancellation/15381 *) https://discuss.ocaml.org/t/confused-about-moonpool-cancellation/15381 but
there is no cancelation anymore :) *)
let ( let@ ) = ( @@ ) let ( let@ ) = ( @@ )
let () = let () =
let@ () = Trace_tef.with_setup () in let@ () = Trace_tef.with_setup () in
let@ _ = Moonpool_fib.main in let@ _ = Moonpool.main in
(* let@ runner = Moonpool.Ws_pool.with_ () in *) (* let@ runner = Moonpool.Ws_pool.with_ () in *)
let@ runner = Moonpool.Background_thread.with_ () in let@ runner = Moonpool.Background_thread.with_ () in
@ -13,15 +14,13 @@ let () =
(* Pretend this is some long-running read loop *) (* Pretend this is some long-running read loop *)
for i = 1 to 10 do for i = 1 to 10 do
Printf.printf "MAIN LOOP %d\n%!" i; Printf.printf "MAIN LOOP %d\n%!" i;
Moonpool_fib.check_if_cancelled (); let _ : _ Moonpool.Fut.t =
let _ : _ Moonpool_fib.t = Moonpool.Fut.spawn ~on:runner (fun () ->
Moonpool_fib.spawn ~on:runner ~protect:false (fun () ->
Printf.printf "RUN FIBER %d\n%!" i; Printf.printf "RUN FIBER %d\n%!" i;
Moonpool_fib.check_if_cancelled ();
Format.printf "FIBER %d NOT CANCELLED YET@." i; Format.printf "FIBER %d NOT CANCELLED YET@." i;
failwith "BOOM") failwith "BOOM")
in in
Moonpool_fib.yield (); Moonpool.Fut.yield ();
(* Thread.delay 0.2; *) (* Thread.delay 0.2; *)
(* Thread.delay 0.0001; *) (* Thread.delay 0.0001; *)
() ()

View file

@ -5,7 +5,6 @@
;(package moonpool) ;(package moonpool)
(libraries (libraries
moonpool moonpool
moonpool.fib
trace trace
trace-tef trace-tef
;tracy-client.trace ;tracy-client.trace