mirror of
https://github.com/ocaml-tracing/ocaml-trace.git
synced 2026-03-07 18:37:56 -05:00
testing with domains
This commit is contained in:
parent
72d64be0c3
commit
3752d70403
5 changed files with 133 additions and 0 deletions
28
test/domains/dune
Normal file
28
test/domains/dune
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
; Marker library: only present on OCaml 5+, used as a proxy for Domain availability.
|
||||||
|
|
||||||
|
(library
|
||||||
|
(name ocaml5)
|
||||||
|
(modules ocaml5)
|
||||||
|
(enabled_if
|
||||||
|
(>= %{ocaml_version} 5)))
|
||||||
|
|
||||||
|
(executable
|
||||||
|
(name t_domains)
|
||||||
|
(modules t_domains)
|
||||||
|
(libraries
|
||||||
|
trace
|
||||||
|
; use the marker library to pick between the real test and the quine fallback.
|
||||||
|
(select
|
||||||
|
t_domains.ml
|
||||||
|
from
|
||||||
|
(ocaml5 threads -> t_domains.real.ml)
|
||||||
|
(-> t_domains.quine.ml))))
|
||||||
|
|
||||||
|
(rule
|
||||||
|
(alias runtest)
|
||||||
|
(action
|
||||||
|
(progn
|
||||||
|
(with-stdout-to
|
||||||
|
t_domains.output
|
||||||
|
(run %{exe:t_domains.exe} %{dep:t_domains.expected}))
|
||||||
|
(diff t_domains.expected t_domains.output))))
|
||||||
1
test/domains/ocaml5.ml
Normal file
1
test/domains/ocaml5.ml
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
(* Marker module: presence indicates OCaml 5+ (and thus Domain availability). *)
|
||||||
5
test/domains/t_domains.expected
Normal file
5
test/domains/t_domains.expected
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
=== domain concurrency ===
|
||||||
|
domain-0: enter=10000 exit=10000 msg=10000
|
||||||
|
domain-1: enter=10000 exit=10000 msg=10000
|
||||||
|
domain-2: enter=10000 exit=10000 msg=10000
|
||||||
|
domain-3: enter=10000 exit=10000 msg=10000
|
||||||
10
test/domains/t_domains.quine.ml
Normal file
10
test/domains/t_domains.quine.ml
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
(* When Domain is unavailable (OCaml < 5), echo the expected file
|
||||||
|
so the diff always passes. The file is passed as argv[1] by the dune rule. *)
|
||||||
|
let () =
|
||||||
|
let ic = open_in Sys.argv.(1) in
|
||||||
|
(try
|
||||||
|
while true do
|
||||||
|
print_char (input_char ic)
|
||||||
|
done
|
||||||
|
with End_of_file -> ());
|
||||||
|
close_in ic
|
||||||
89
test/domains/t_domains.real.ml
Normal file
89
test/domains/t_domains.real.ml
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
(* Test concurrent span recording across multiple domains (OCaml 5+).
|
||||||
|
Each domain runs [iters] spans. We embed the recording thread's id into
|
||||||
|
the span value so that exit events can be attributed to the right domain,
|
||||||
|
then verify that every domain has exactly [iters] enters and exits. *)
|
||||||
|
|
||||||
|
let ( let@ ) = ( @@ )
|
||||||
|
let iters = 10_000
|
||||||
|
|
||||||
|
(* Custom span type that carries the thread id of the recording domain. *)
|
||||||
|
type Trace_core.span += Thread_span of int
|
||||||
|
|
||||||
|
let make_recorder () =
|
||||||
|
let log = Queue.create () in
|
||||||
|
let mu = Mutex.create () in
|
||||||
|
let add x =
|
||||||
|
Mutex.lock mu;
|
||||||
|
Queue.add x log;
|
||||||
|
Mutex.unlock mu
|
||||||
|
in
|
||||||
|
let enter_span () ~__FUNCTION__:_ ~__FILE__:_ ~__LINE__:_ ~level:_ ~params:_
|
||||||
|
~data:_ ~parent:_ _name =
|
||||||
|
let tid = Thread.id (Thread.self ()) in
|
||||||
|
add (`Enter tid);
|
||||||
|
Thread_span tid
|
||||||
|
in
|
||||||
|
let exit_span () sp =
|
||||||
|
match sp with
|
||||||
|
| Thread_span tid -> add (`Exit tid)
|
||||||
|
| _ -> ()
|
||||||
|
in
|
||||||
|
let message () ~level:_ ~params:_ ~data:_ ~span:_ _msg =
|
||||||
|
add (`Msg (Thread.id (Thread.self ())))
|
||||||
|
in
|
||||||
|
let metric () ~level:_ ~params:_ ~data:_ _name _m = () in
|
||||||
|
let add_data_to_span () _sp _data = () in
|
||||||
|
let coll =
|
||||||
|
Trace_core.Collector.(
|
||||||
|
C_some
|
||||||
|
( (),
|
||||||
|
Callbacks.make ~enter_span ~exit_span ~add_data_to_span ~message
|
||||||
|
~metric () ))
|
||||||
|
in
|
||||||
|
coll, fun () -> Queue.fold (fun acc x -> x :: acc) [] log |> List.rev
|
||||||
|
|
||||||
|
let () =
|
||||||
|
print_endline "=== domain concurrency ===";
|
||||||
|
let coll, get = make_recorder () in
|
||||||
|
let@ () = Trace_core.with_setup_collector coll in
|
||||||
|
let n = 4 in
|
||||||
|
(* Each domain returns its own thread id so we can check per-domain counts. *)
|
||||||
|
let domains =
|
||||||
|
Array.init n (fun _i ->
|
||||||
|
Domain.spawn (fun () ->
|
||||||
|
let tid = Thread.id (Thread.self ()) in
|
||||||
|
for j = 1 to iters do
|
||||||
|
Trace_core.with_span ~__FILE__ ~__LINE__ "" @@ fun _ ->
|
||||||
|
Trace_core.message "";
|
||||||
|
if j mod 1000 = 0 then Thread.yield ()
|
||||||
|
done;
|
||||||
|
tid))
|
||||||
|
in
|
||||||
|
let tids = Array.map Domain.join domains in
|
||||||
|
let events = get () in
|
||||||
|
let count pred = List.length (List.filter pred events) in
|
||||||
|
(* For each domain, verify exactly [iters] enters and [iters] exits. *)
|
||||||
|
Array.iteri
|
||||||
|
(fun i tid ->
|
||||||
|
let n_enter =
|
||||||
|
count (function
|
||||||
|
| `Enter t -> t = tid
|
||||||
|
| _ -> false)
|
||||||
|
in
|
||||||
|
let n_exit =
|
||||||
|
count (function
|
||||||
|
| `Exit t -> t = tid
|
||||||
|
| _ -> false)
|
||||||
|
in
|
||||||
|
let n_msg =
|
||||||
|
count (function
|
||||||
|
| `Msg t -> t = tid
|
||||||
|
| _ -> false)
|
||||||
|
in
|
||||||
|
Printf.printf "domain-%d: enter=%d exit=%d msg=%d%s\n" i n_enter n_exit
|
||||||
|
n_msg
|
||||||
|
(if n_enter = iters && n_exit = iters && n_msg = iters then
|
||||||
|
""
|
||||||
|
else
|
||||||
|
" FAIL"))
|
||||||
|
tids
|
||||||
Loading…
Add table
Reference in a new issue