Skip to content

Commit 9067c6b

Browse files
committed
adapt some tests for the lwt runner
1 parent 52bf102 commit 9067c6b

File tree

13 files changed

+4362
-342
lines changed

13 files changed

+4362
-342
lines changed

test/fiber/dune

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
(>= %{ocaml_version} 5.0))
55
(package moonpool)
66
(libraries
7+
t_fibers
78
moonpool
89
moonpool.fib
910
trace

test/fiber/lib/dune

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
(library
2+
(name t_fibers)
3+
(enabled_if
4+
(>= %{ocaml_version} 5.0))
5+
(package moonpool)
6+
(libraries
7+
moonpool
8+
moonpool.fib
9+
trace
10+
qcheck-core))
11+
12+

test/fiber/lib/fib.ml

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
2+
open! Moonpool
3+
module A = Atomic
4+
module F = Moonpool_fib.Fiber
5+
6+
let ( let@ ) = ( @@ )
7+
8+
module TS = struct
9+
type t = int list
10+
11+
let show (s : t) = String.concat "." @@ List.map string_of_int s
12+
let init = [ 0 ]
13+
14+
let next_ = function
15+
| [] -> [ 0 ]
16+
| n :: tl -> (n + 1) :: tl
17+
18+
let tick (t : t ref) = t := next_ !t
19+
20+
let tick_get t =
21+
tick t;
22+
!t
23+
end
24+
25+
(* more deterministic logging of events *)
26+
module Log_ = struct
27+
let events : (TS.t * string) list A.t = A.make []
28+
29+
let add_event t msg : unit =
30+
while
31+
let old = A.get events in
32+
not (A.compare_and_set events old ((t, msg) :: old))
33+
do
34+
()
35+
done
36+
37+
let logf t fmt = Printf.ksprintf (add_event t) fmt
38+
39+
let print_and_clear () =
40+
let l =
41+
A.exchange events []
42+
|> List.map (fun (ts, msg) -> List.rev ts, msg)
43+
|> List.sort Stdlib.compare
44+
in
45+
List.iter (fun (ts, msg) -> Printf.printf "%s: %s\n" (TS.show ts) msg) l
46+
end
47+
48+
let logf = Log_.logf
49+
50+
let run1 ~runner () =
51+
Printf.printf "============\nstart\n%!";
52+
let clock = ref TS.init in
53+
let fib =
54+
F.spawn_top ~on:runner @@ fun () ->
55+
let chan_progress = Chan.create ~max_size:4 () in
56+
let chans = Array.init 5 (fun _ -> Chan.create ~max_size:4 ()) in
57+
58+
let subs =
59+
List.init 5 (fun i ->
60+
F.spawn ~protect:false @@ fun _n ->
61+
Thread.delay (float i *. 0.01);
62+
Chan.pop chans.(i);
63+
Chan.push chan_progress i;
64+
F.check_if_cancelled ();
65+
i)
66+
in
67+
68+
logf (TS.tick_get clock) "wait for subs";
69+
70+
F.spawn_ignore (fun () ->
71+
for i = 0 to 4 do
72+
Chan.push chans.(i) ();
73+
let i' = Chan.pop chan_progress in
74+
assert (i = i')
75+
done);
76+
77+
(let clock0 = !clock in
78+
List.iteri
79+
(fun i f ->
80+
let clock = ref (0 :: i :: clock0) in
81+
logf !clock "await fiber %d" i;
82+
logf (TS.tick_get clock) "cur fiber[%d] is some: %b" i
83+
(Option.is_some @@ F.Private_.get_cur_opt ());
84+
let res = F.await f in
85+
logf (TS.tick_get clock) "cur fiber[%d] is some: %b" i
86+
(Option.is_some @@ F.Private_.get_cur_opt ());
87+
F.yield ();
88+
logf (TS.tick_get clock) "res %d = %d" i res)
89+
subs);
90+
91+
logf (TS.tick_get clock) "main fiber done"
92+
in
93+
94+
Fut.await @@ F.res fib;
95+
logf (TS.tick_get clock) "main fiber exited";
96+
Log_.print_and_clear ();
97+
()
98+
99+
let run2 ~runner () =
100+
(* same but now, cancel one of the sub-fibers *)
101+
Printf.printf "============\nstart\n";
102+
103+
let clock = ref TS.init in
104+
let fib =
105+
F.spawn_top ~on:runner @@ fun () ->
106+
let@ () =
107+
F.with_on_self_cancel (fun ebt ->
108+
logf (TS.tick_get clock) "main fiber cancelled with %s"
109+
@@ Exn_bt.show ebt)
110+
in
111+
112+
let chans_unblock = Array.init 10 (fun _i -> Chan.create ~max_size:4 ()) in
113+
let chan_progress = Chan.create ~max_size:4 () in
114+
115+
logf (TS.tick_get clock) "start fibers";
116+
let subs =
117+
let clock0 = !clock in
118+
List.init 10 (fun i ->
119+
let clock = ref (0 :: i :: clock0) in
120+
F.spawn ~protect:false @@ fun _n ->
121+
let@ () =
122+
F.with_on_self_cancel (fun _ ->
123+
logf (TS.tick_get clock) "sub-fiber %d was cancelled" i)
124+
in
125+
Thread.delay 0.002;
126+
127+
(* sync for determinism *)
128+
Chan.pop chans_unblock.(i);
129+
Chan.push chan_progress i;
130+
131+
if i = 7 then (
132+
logf (TS.tick_get clock) "I'm fiber %d and I'm about to fail…" i;
133+
failwith "oh no!"
134+
);
135+
136+
F.check_if_cancelled ();
137+
i)
138+
in
139+
140+
let post = TS.tick_get clock in
141+
List.iteri
142+
(fun i fib ->
143+
F.on_result fib (function
144+
| Ok _ -> logf (i :: post) "fiber %d resolved as ok" i
145+
| Error _ -> logf (i :: post) "fiber %d resolved as error" i))
146+
subs;
147+
148+
(* sequentialize the fibers, for determinism *)
149+
F.spawn_ignore (fun () ->
150+
for j = 0 to 9 do
151+
Chan.push chans_unblock.(j) ();
152+
let j' = Chan.pop chan_progress in
153+
assert (j = j')
154+
done);
155+
156+
logf (TS.tick_get clock) "wait for subs";
157+
List.iteri
158+
(fun i f ->
159+
logf (TS.tick_get clock) "await fiber %d" i;
160+
let res = F.await f in
161+
logf (TS.tick_get clock) "res %d = %d" i res)
162+
subs;
163+
logf (TS.tick_get clock) "yield";
164+
F.yield ();
165+
logf (TS.tick_get clock) "yielded";
166+
logf (TS.tick_get clock) "main fiber done"
167+
in
168+
169+
F.on_result fib (function
170+
| Ok () -> logf (TS.tick_get clock) "main fiber result: ok"
171+
| Error ebt ->
172+
logf (TS.tick_get clock) "main fiber result: error %s" (Exn_bt.show ebt));
173+
174+
(try Fut.await @@ F.res fib
175+
with Failure msg -> logf (TS.tick_get clock) "main fib failed with %S" msg);
176+
logf (TS.tick_get clock) "main fiber exited";
177+
Log_.print_and_clear ();
178+
()

test/fiber/lib/fls.ml

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
2+
open! Moonpool
3+
module A = Atomic
4+
module F = Moonpool_fib.Fiber
5+
module FLS = Moonpool_fib.Fls
6+
7+
(* ### dummy little tracing system with local storage *)
8+
9+
type span_id = int
10+
11+
let k_parent : span_id Hmap.key = Hmap.Key.create ()
12+
let ( let@ ) = ( @@ )
13+
let spf = Printf.sprintf
14+
15+
module Span = struct
16+
let new_id_ : unit -> span_id =
17+
let n = A.make 0 in
18+
fun () -> A.fetch_and_add n 1
19+
20+
type t = {
21+
id: span_id;
22+
parent: span_id option;
23+
msg: string;
24+
}
25+
end
26+
27+
module Tracer = struct
28+
type t = { spans: Span.t list A.t }
29+
30+
let create () : t = { spans = A.make [] }
31+
let get self = A.get self.spans
32+
33+
let add (self : t) span =
34+
while
35+
let old = A.get self.spans in
36+
not (A.compare_and_set self.spans old (span :: old))
37+
do
38+
()
39+
done
40+
41+
let with_span self name f =
42+
let id = Span.new_id_ () in
43+
let parent = FLS.get_in_local_hmap_opt k_parent in
44+
let span = { Span.id; parent; msg = name } in
45+
add self span;
46+
FLS.with_in_local_hmap k_parent id f
47+
end
48+
49+
module Render = struct
50+
type span_tree = {
51+
msg: string; (** message of the span at the root *)
52+
children: span_tree list;
53+
}
54+
55+
type t = { roots: span_tree list }
56+
57+
let build (tracer : Tracer.t) : t =
58+
let tops : (span_id, Span.t) Hashtbl.t = Hashtbl.create 16 in
59+
let children : (span_id, Span.t list) Hashtbl.t = Hashtbl.create 16 in
60+
61+
(* everyone is a root at first *)
62+
let all_spans = Tracer.get tracer in
63+
List.iter (fun (sp : Span.t) -> Hashtbl.add tops sp.id sp) all_spans;
64+
65+
(* now consider the parenting relationships *)
66+
let add_span_to_parent (span : Span.t) =
67+
match span.parent with
68+
| None -> ()
69+
| Some p ->
70+
Hashtbl.remove tops span.id;
71+
let l = try Hashtbl.find children p with Not_found -> [] in
72+
Hashtbl.replace children p (span :: l)
73+
in
74+
List.iter add_span_to_parent all_spans;
75+
76+
(* build the tree *)
77+
let rec build_tree (sp : Span.t) : span_tree =
78+
let children = try Hashtbl.find children sp.id with Not_found -> [] in
79+
let children = List.map build_tree children |> List.sort Stdlib.compare in
80+
{ msg = sp.msg; children }
81+
in
82+
83+
let roots =
84+
Hashtbl.fold (fun _ sp l -> build_tree sp :: l) tops []
85+
|> List.sort Stdlib.compare
86+
in
87+
88+
{ roots }
89+
90+
let pp (oc : out_channel) (self : t) : unit =
91+
let rec pp_tree indent out (t : span_tree) =
92+
let prefix = String.make indent ' ' in
93+
Printf.fprintf out "%s%S\n" prefix t.msg;
94+
List.iter (pp_tree (indent + 2) out) t.children
95+
in
96+
List.iter (pp_tree 2 oc) self.roots
97+
end
98+
99+
let run ~pool ~pool_name () =
100+
let tracer = Tracer.create () in
101+
102+
let sub_sub_child ~idx ~idx_child ~idx_sub ~idx_sub_sub () =
103+
let@ () =
104+
Tracer.with_span tracer
105+
(spf "child_%d.%d.%d.%d" idx idx_child idx_sub idx_sub_sub)
106+
in
107+
108+
for j = 1 to 5 do
109+
let@ () = Tracer.with_span tracer (spf "iter.loop %d" j) in
110+
F.yield ()
111+
done
112+
in
113+
114+
let sub_child ~idx ~idx_child ~idx_sub () =
115+
let@ () =
116+
Tracer.with_span tracer (spf "child_%d.%d.%d" idx idx_child idx_sub)
117+
in
118+
119+
for i = 1 to 10 do
120+
let@ () = Tracer.with_span tracer (spf "iter.loop %02d" i) in
121+
F.yield ()
122+
done;
123+
124+
let subs =
125+
List.init 2 (fun idx_sub_sub ->
126+
F.spawn ~protect:true (fun () ->
127+
sub_sub_child ~idx ~idx_child ~idx_sub ~idx_sub_sub ()))
128+
in
129+
List.iter F.await subs
130+
in
131+
132+
let top_child ~idx ~idx_child () =
133+
let@ () = Tracer.with_span tracer (spf "child.%d.%d" idx idx_child) in
134+
135+
let subs =
136+
List.init 2 (fun k ->
137+
F.spawn ~protect:true @@ fun () ->
138+
sub_child ~idx ~idx_child ~idx_sub:k ())
139+
in
140+
141+
let@ () =
142+
Tracer.with_span tracer
143+
(spf "child.%d.%d.99.await_children" idx idx_child)
144+
in
145+
List.iter F.await subs
146+
in
147+
148+
let top idx =
149+
let@ () = Tracer.with_span tracer (spf "top_%d" idx) in
150+
151+
let subs =
152+
List.init 5 (fun j ->
153+
F.spawn ~protect:true @@ fun () -> top_child ~idx ~idx_child:j ())
154+
in
155+
156+
List.iter F.await subs
157+
in
158+
159+
Printf.printf "run test on pool = %s\n" pool_name;
160+
let fibs =
161+
List.init 8 (fun idx -> F.spawn_top ~on:pool (fun () -> top idx))
162+
in
163+
List.iter F.wait_block_exn fibs;
164+
165+
Printf.printf "tracing complete\n";
166+
Printf.printf "spans:\n";
167+
let tree = Render.build tracer in
168+
Render.pp stdout tree;
169+
Printf.printf "done\n%!";
170+
()

0 commit comments

Comments
 (0)