Improve traceln formatting

- Set up a box so that wrapping works correctly.
- Print a `+` at the start of each trace line to show it is trace output.
- Format location tag with the rest of the output (fixes wrapping).
- Add tests for traceln.
This commit is contained in:
Thomas Leonard 2021-07-15 11:26:01 +01:00
parent 8a1b2c6d82
commit 8f5043c458
11 changed files with 210 additions and 184 deletions

View File

@ -137,7 +137,7 @@ e.g.
let buffer = Buffer.create 20 in
main ~stdout:(Eio.Flow.buffer_sink buffer);
traceln "Main would print %S" (Buffer.contents buffer);;
Main would print "Hello, world!\n"
+Main would print "Hello, world!\n"
- : unit = ()
```
@ -158,12 +158,12 @@ let main _env =
```ocaml
# Eio_main.run main;;
x = 1
y = 1
x = 2
y = 2
x = 3
y = 3
+x = 1
+y = 1
+x = 2
+y = 2
+x = 3
+y = 3
- : unit = ()
```
@ -186,12 +186,12 @@ We can run the previous code with tracing enabled (writing to a new `trace.ctf`
Ctf.Control.start trace_config;
Eio_main.run main;
Ctf.Control.stop trace_config;;
x = 1
y = 1
x = 2
y = 2
x = 3
y = 3
+x = 1
+y = 1
+x = 2
+y = 2
+x = 3
+y = 3
```
The trace can be viewed using [mirage-trace-viewer][].
@ -218,7 +218,7 @@ Here's what happens if one of the two threads above fails:
Fibre.both ~sw
(fun () -> for x = 1 to 3 do traceln "x = %d" x; Fibre.yield ~sw () done)
(fun () -> failwith "Simulated error");;
x = 1
+x = 1
Exception: Failure "Simulated error".
```
@ -248,15 +248,15 @@ Switches can also be used to wait for threads even when there isn't an error. e.
traceln "Second thread forked; top-level code is finished"
);
traceln "Switch is finished";;
i = 1
First thread forked
j = 1
i = 2
Second thread forked; top-level code is finished
j = 2
i = 3
j = 3
Switch is finished
+i = 1
+First thread forked
+j = 1
+i = 2
+Second thread forked; top-level code is finished
+j = 2
+i = 3
+j = 3
+Switch is finished
- : unit = ()
```
@ -383,11 +383,11 @@ let main ~net ~addr =
main
~net:(Eio.Stdenv.net env)
~addr:(`Tcp (Unix.inet_addr_loopback, 8080))
Server ready...
Connecting to server...
Server accepted connection from client
(normally we'd loop and accept more connections here)
Server received: "Hello from client"
+Server ready...
+Connecting to server...
+Server accepted connection from client
+(normally we'd loop and accept more connections here)
+Server received: "Hello from client"
- : unit = ()
```
@ -464,9 +464,9 @@ let try_mkdir dir path =
try_mkdir cwd "dir1";
try_mkdir cwd "../dir2";
try_mkdir cwd "/tmp/dir3";
mkdir "dir1" -> ok
mkdir "../dir2" -> Eio.Dir.Permission_denied("..", _)
mkdir "/tmp/dir3" -> Eio.Dir.Permission_denied("/tmp", _)
+mkdir "dir1" -> ok
+mkdir "../dir2" -> Eio.Dir.Permission_denied("..", _)
+mkdir "/tmp/dir3" -> Eio.Dir.Permission_denied("/tmp", _)
- : unit = ()
```
@ -481,9 +481,9 @@ The checks also apply to following symlinks:
try_write_file cwd "dir1/file1" "A";
try_write_file cwd "link-to-dir1/file2" "B";
try_write_file cwd "link-to-tmp/file3" "C"
write "dir1/file1" -> ok
write "link-to-dir1/file2" -> ok
write "link-to-tmp/file3" -> Eio.Dir.Permission_denied("link-to-tmp/file3", _)
+write "dir1/file1" -> ok
+write "link-to-dir1/file2" -> ok
+write "link-to-tmp/file3" -> Eio.Dir.Permission_denied("link-to-tmp/file3", _)
- : unit = ()
```
@ -495,8 +495,8 @@ You can use `open_dir` (or `with_open_dir`) to create a restricted capability to
Eio.Dir.with_open_dir cwd "dir1" @@ fun dir1 ->
try_write_file dir1 "file4" "D";
try_write_file dir1 "../file5" "E"
write "file4" -> ok
write "../file5" -> Eio.Dir.Permission_denied("../file5", _)
+write "file4" -> ok
+write "../file5" -> Eio.Dir.Permission_denied("../file5", _)
- : unit = ()
```
@ -518,8 +518,8 @@ The standard environment provides a clock with the usual POSIX time:
traceln "The time is now %f" (Eio.Time.now clock);
Eio.Time.sleep clock 1.0;
traceln "The time is now %f" (Eio.Time.now clock)
The time is now 1623940778.270336
The time is now 1623940779.270336
+The time is now 1623940778.270336
+The time is now 1623940779.270336
- : unit = ()
```
@ -562,12 +562,12 @@ let main ~domain_mgr =
```ocaml
# Eio_main.run @@ fun env ->
main ~domain_mgr:(Eio.Stdenv.domain_mgr env)
Starting CPU-intensive task...
Starting CPU-intensive task...
Finished
sum 1..50000 = 1250025000
Finished
sum 1..100000 = 5000050000
+Starting CPU-intensive task...
+Starting CPU-intensive task...
+Finished
+sum 1..50000 = 1250025000
+Finished
+sum 1..100000 = 5000050000
- : unit = ()
```

View File

@ -44,20 +44,6 @@ let rec shiftv cs = function
effect Close : Unix.file_descr -> int
let stderr_mutex = Mutex.create ()
let default_traceln ?__POS__ fmt =
fmt |> Format.kasprintf (fun msg ->
Ctf.label msg;
Mutex.lock stderr_mutex;
Fun.protect ~finally:(fun () -> Mutex.unlock stderr_mutex)
(fun () ->
match __POS__ with
| Some (file, lnum, _, _) -> Format.printf "%s:%d %s@." file lnum msg
| None -> Format.printf "%s@." msg
)
)
module FD = struct
type t = {
seekable : bool;
@ -635,7 +621,7 @@ let accept ~sw fd =
let run_compute fn () =
match fn () with
| x -> x
| effect Eio.Private.Effects.Trace k -> continue k default_traceln
| effect Eio.Private.Effects.Trace k -> continue k Eunix.Trace.default_traceln
module Objects = struct
type _ Eio.Generic.ty += FD : FD.t Eio.Generic.ty
@ -976,7 +962,7 @@ let run ?(queue_depth=64) ?(block_size=4096) main =
| exception ex ->
Ctf.note_resolved child ~ex:(Some ex)
)
| effect Eio.Private.Effects.Trace k -> continue k default_traceln
| effect Eio.Private.Effects.Trace k -> continue k Eunix.Trace.default_traceln
| effect Alloc k ->
let k = { Suspended.k; tid } in
alloc_buf st k

17
lib_eunix/trace.ml Normal file
View File

@ -0,0 +1,17 @@
let mutex = Mutex.create ()
let default_traceln ?__POS__:pos fmt =
let b = Buffer.create 512 in
let k f =
Option.iter (fun (file, lnum, _, _) -> Format.fprintf f " [%s:%d]" file lnum) pos;
Format.pp_close_box f ();
Format.pp_print_flush f ();
let msg = Buffer.contents b in
Ctf.label msg;
let lines = String.split_on_char '\n' msg in
Mutex.lock mutex;
Fun.protect ~finally:(fun () -> Mutex.unlock mutex) @@ fun () ->
List.iter (Printf.eprintf "+%s\n") lines;
flush stderr
in
Format.kfprintf k (Format.formatter_of_buffer b) ("@[" ^^ fmt)

9
lib_eunix/trace.mli Normal file
View File

@ -0,0 +1,9 @@
val mutex : Mutex.t
val default_traceln :
?__POS__:string * int * int * int ->
('a, Format.formatter, unit, unit) format4 -> 'a
(** [default_traceln] is a suitable default implementation for {!Eio.Std.traceln}.
It writes output to stderr, prefixing each line with a "+".
If [__POS__] is given, it also displays the file and line number from that
It uses {!mutex} so that only one domain's output is written at a time. *)

View File

@ -20,7 +20,7 @@ Spawning a second domain:
# run @@ fun mgr ->
let response = Eio.Domain_manager.run_compute_unsafe mgr (fun () -> "Hello from new domain") in
traceln "Got %S from spawned domain" response
Got "Hello from new domain" from spawned domain
+Got "Hello from new domain" from spawned domain
- : unit = ()
```
@ -46,8 +46,8 @@ We can still run other fibres in the main domain while waiting:
(fun () ->
traceln "Other fibres can still run"
)
Spawning new domain...
Other fibres can still run
Got "Hello from new domain" from spawned domain
+Spawning new domain...
+Other fibres can still run
+Got "Hello from new domain" from spawned domain
- : unit = ()
```

View File

@ -49,7 +49,7 @@ Creating a file and reading it back:
let cwd = Eio.Stdenv.cwd env in
write_file ~sw ~create:(`Exclusive 0o666) cwd "test-file" "my-data";
traceln "Got %S" @@ read_file ~sw cwd "test-file"
Got "my-data"
+Got "my-data"
- : unit = ()
```
@ -103,7 +103,7 @@ If-missing create succeeds if already exists:
write_file ~sw ~create:(`If_missing 0o666) cwd "test-file" "1st-write-original";
write_file ~sw ~create:(`If_missing 0o666) cwd "test-file" "2nd-write";
traceln "Got %S" @@ read_file ~sw cwd "test-file"
Got "2nd-write-original"
+Got "2nd-write-original"
- : unit = ()
```
@ -114,7 +114,7 @@ Truncate create succeeds if already exists, and truncates:
write_file ~sw ~create:(`Or_truncate 0o666) cwd "test-file" "1st-write-original";
write_file ~sw ~create:(`Or_truncate 0o666) cwd "test-file" "2nd-write";
traceln "Got %S" @@ read_file ~sw cwd "test-file"
Got "2nd-write"
+Got "2nd-write"
- : unit = ()
# Unix.unlink "test-file";;
- : unit = ()
@ -136,7 +136,7 @@ Appending to an existing file:
write_file ~sw ~create:(`Or_truncate 0o666) cwd "test-file" "1st-write-original";
write_file ~sw ~create:`Never ~append:true cwd "test-file" "2nd-write";
traceln "Got %S" @@ read_file ~sw cwd "test-file"
Got "1st-write-original2nd-write"
+Got "1st-write-original2nd-write"
- : unit = ()
# Unix.unlink "test-file";;
- : unit = ()
@ -151,8 +151,8 @@ Got "1st-write-original2nd-write"
try_mkdir cwd "subdir/nested";
write_file ~sw ~create:(`Exclusive 0o600) cwd "subdir/nested/test-file" "data";
()
mkdir "subdir" -> ok
mkdir "subdir/nested" -> ok
+mkdir "subdir" -> ok
+mkdir "subdir/nested" -> ok
- : unit = ()
# Unix.unlink "subdir/nested/test-file"; Unix.rmdir "subdir/nested"; Unix.rmdir "subdir";;
- : unit = ()
@ -175,12 +175,12 @@ Creating directories with nesting, symlinks, etc:
try_mkdir cwd "to-subdir";
try_mkdir cwd "dangle/foo";
()
mkdir "subdir" -> ok
mkdir "to-subdir/nested" -> ok
mkdir "to-root/tmp/foo" -> Eio.Dir.Permission_denied("to-root/tmp", _)
mkdir "../foo" -> Eio.Dir.Permission_denied("..", _)
mkdir "to-subdir" -> Unix.Unix_error(Unix.EEXIST, "mkdirat", "to-subdir")
mkdir "dangle/foo" -> Unix.Unix_error(Unix.ENOENT, "openat2", "")
+mkdir "subdir" -> ok
+mkdir "to-subdir/nested" -> ok
+mkdir "to-root/tmp/foo" -> Eio.Dir.Permission_denied("to-root/tmp", _)
+mkdir "../foo" -> Eio.Dir.Permission_denied("..", _)
+mkdir "to-subdir" -> Unix.Unix_error(Unix.EEXIST, "mkdirat", "to-subdir")
+mkdir "dangle/foo" -> Unix.Unix_error(Unix.ENOENT, "openat2", "")
- : unit = ()
```
@ -195,9 +195,9 @@ Create a sandbox, write a file with it, then read it from outside:
write_file ~sw ~create:(`Exclusive 0o600) subdir "test-file" "data";
try_mkdir subdir "../new-sandbox";
traceln "Got %S" @@ read_file ~sw cwd "sandbox/test-file"
mkdir "sandbox" -> ok
mkdir "../new-sandbox" -> Eio.Dir.Permission_denied("..", _)
Got "data"
+mkdir "sandbox" -> ok
+mkdir "../new-sandbox" -> Eio.Dir.Permission_denied("..", _)
+Got "data"
- : unit = ()
```
@ -219,12 +219,12 @@ Using `cwd` we can't access the parent, but using `fs` we can:
);
Unix.unlink "test-file";
Unix.rmdir "outside-cwd"
mkdir "fs-test" -> ok
chdir "fs-test"
mkdir "../outside-cwd" -> Eio.Dir.Permission_denied("..", _)
write "../test-file" -> Eio.Dir.Permission_denied("../test-file", _)
mkdir "../outside-cwd" -> ok
write "../test-file" -> ok
chdir ".."
+mkdir "fs-test" -> ok
+chdir "fs-test"
+mkdir "../outside-cwd" -> Eio.Dir.Permission_denied("..", _)
+write "../test-file" -> Eio.Dir.Permission_denied("../test-file", _)
+mkdir "../outside-cwd" -> ok
+write "../test-file" -> ok
+chdir ".."
- : unit = ()
```

View File

@ -69,11 +69,11 @@ Handling one connection, then cancelling the server:
```ocaml
# run (test_address addr)
Connecting to server...
Server accepted connection from client
Server received: "Hello from client"
Client received: "Bye"
Client finished - cancelling server
+Connecting to server...
+Server accepted connection from client
+Server received: "Hello from client"
+Client received: "Bye"
+Client finished - cancelling server
Exception: Failure "Test is over".
```
@ -81,11 +81,11 @@ Handling one connection on a Unix domain socket:
```ocaml
# run (test_address (`Unix "/tmp/eio-test.sock"))
Connecting to server...
Server accepted connection from client
Server received: "Hello from client"
Client received: "Bye"
Client finished - cancelling server
+Connecting to server...
+Server accepted connection from client
+Server received: "Hello from client"
+Client received: "Bye"
+Client finished - cancelling server
Exception: Failure "Test is over".
```
@ -93,11 +93,11 @@ Handling one connection on an abstract Unix domain socket:
```ocaml
# run (test_address (`Unix "\x00/tmp/eio-test.sock"))
Connecting to server...
Server accepted connection from client
Server received: "Hello from client"
Client received: "Bye"
Client finished - cancelling server
+Connecting to server...
+Server accepted connection from client
+Server received: "Hello from client"
+Client received: "Bye"
+Client finished - cancelling server
Exception: Failure "Test is over".
```
@ -125,9 +125,9 @@ Cancelling the read:
let msg = read_all flow in
traceln "Client received: %S" msg
)
Connecting to server...
Connection opened - cancelling server's read
Client received: "Request cancelled"
+Connecting to server...
+Connection opened - cancelling server's read
+Client received: "Request cancelled"
Exception: Graceful_shutdown.
```

View File

@ -20,7 +20,7 @@ A very basic example:
# run (fun _sw ->
traceln "Running"
);
Running
+Running
- : unit = ()
```
@ -32,8 +32,8 @@ Turning off a switch still allows you to perform clean-up operations:
Switch.turn_off sw (Failure "Cancel");
traceln "Clean up"
);
Running
Clean up
+Running
+Clean up
Exception: Failure "Cancel".
```
@ -45,10 +45,10 @@ Exception: Failure "Cancel".
(fun () -> for i = 1 to 2 do traceln "i = %d" i; Fibre.yield ~sw () done)
(fun () -> for j = 1 to 2 do traceln "j = %d" j; Fibre.yield ~sw () done)
);
i = 1
j = 1
i = 2
j = 2
+i = 1
+j = 1
+i = 2
+j = 2
- : unit = ()
```
@ -60,7 +60,7 @@ j = 2
(fun () -> for i = 1 to 5 do traceln "i = %d" i; Fibre.yield ~sw () done)
(fun () -> failwith "Failed")
)
i = 1
+i = 1
Exception: Failure "Failed".
```
@ -72,7 +72,7 @@ Exception: Failure "Failed".
(fun () -> Fibre.yield ~sw (); failwith "Failed")
(fun () -> for i = 1 to 5 do traceln "i = %d" i; Fibre.yield ~sw () done)
)
i = 1
+i = 1
Exception: Failure "Failed".
```
@ -118,7 +118,7 @@ The switch is already turned off when we try to fork. The new fibre doesn't star
Fibre.fork_ignore ~sw (fun () -> traceln "Not reached");
traceln "Main continues"
)
Main continues
+Main continues
Exception: Failure "Cancel".
```
@ -148,9 +148,9 @@ Turning off a switch runs the cancel callbacks, unless they've been removed by t
Switch.remove_hook h3;
Switch.remove_hook h4
)
Cancel 3
Cancel 1
Cancel 4
+Cancel 3
+Cancel 1
+Cancel 4
Exception: Failure "Cancelled".
```
@ -162,7 +162,7 @@ Wait for either a promise or a switch; switch cancelled first:
Switch.turn_off sw (Failure "Cancelled");
Promise.fulfill r ()
)
Waiting
+Waiting
Exception: Failure "Cancelled".
```
@ -177,9 +177,9 @@ Wait for either a promise or a switch; promise resolves first:
traceln "Now cancelling...";
Switch.turn_off sw (Failure "Cancelled")
);
Waiting
Resolved
Now cancelling...
+Waiting
+Resolved
+Now cancelling...
Exception: Failure "Cancelled".
```
@ -192,7 +192,7 @@ Wait for either a promise or a switch; switch cancelled first. Result version.
Switch.turn_off sw (Failure "Cancelled");
Promise.fulfill r ()
);
Waiting
+Waiting
Exception: Failure "Cancelled".
```
@ -206,8 +206,8 @@ Wait for either a promise or a switch; promise resolves first but switch off wit
traceln "Now cancelling...";
Switch.turn_off sw (Failure "Cancelled")
)
Waiting
Now cancelling...
+Waiting
+Now cancelling...
Exception: Failure "Cancelled".
```
@ -221,10 +221,10 @@ Child switches are cancelled when the parent is cancelled:
Fibre.fork_sub_ignore ~sw ~on_error (fun sw -> traceln "Child 2"; Promise.await ~sw p);
Switch.turn_off sw (Failure "Cancel parent")
)
Child 1
Child 2
child: Failure("Cancel parent")
child: Failure("Cancel parent")
+Child 1
+Child 2
+child: Failure("Cancel parent")
+child: Failure("Cancel parent")
Exception: Failure "Cancel parent".
```
@ -242,10 +242,10 @@ A child can fail independently of the parent:
Fibre.yield ~sw ();
traceln "Parent fibre is still running"
)
Child 1
Child 2
child: Failure("Child error")
Parent fibre is still running
+Child 1
+Child 2
+child: Failure("Child error")
+Parent fibre is still running
- : unit = ()
```
@ -265,9 +265,9 @@ A child can be cancelled independently of the parent:
Fibre.yield ~sw ();
traceln "Parent fibre is still running"
);
Child 1
child: Failure("Cancel child")
Parent fibre is still running
+Child 1
+child: Failure("Cancel child")
+Parent fibre is still running
- : unit = ()
```
@ -282,7 +282,7 @@ A child error handle raises:
Fibre.yield ~sw ();
traceln "Not reached"
)
Child
+Child
Exception: Failure "Child error escapes".
```
@ -294,8 +294,8 @@ A child error handler deals with the exception:
let x = Switch.sub sw ~on_error:print (fun _sw -> failwith "Child error") in
traceln "x = %d" x
)
Failure("Child error")
x = 0
+Failure("Child error")
+x = 0
- : unit = ()
```
@ -320,8 +320,8 @@ Release on success:
Switch.on_release sw (fun () -> traceln "release 1");
Switch.on_release sw (fun () -> traceln "release 2");
)
release 2
release 1
+release 2
+release 1
- : unit = ()
```
@ -333,8 +333,8 @@ Release on error:
Switch.on_release sw (fun () -> traceln "release 2");
failwith "Test error"
)
release 2
release 1
+release 2
+release 1
Exception: Failure "Test error".
```
@ -346,9 +346,9 @@ A release operation itself fails:
Switch.on_release sw (fun () -> traceln "release 2");
Switch.on_release sw (fun () -> traceln "release 3"; failwith "failure 3");
)
release 3
release 2
release 1
+release 3
+release 2
+release 1
Exception: Multiple exceptions:
Failure("failure 3")
and
@ -376,12 +376,12 @@ Using switch from inside release handler:
);
traceln "Main fibre done"
)
Main fibre done
Starting release 2
Starting release 1
Finished release 2
Finished release 1
Late release
+Main fibre done
+Starting release 2
+Starting release 1
+Finished release 2
+Finished release 1
+Late release
- : unit = ()
```
@ -401,9 +401,9 @@ We release when `fork_sub_ignore` returns:
# run (fun sw ->
fork_sub_ignore_resource sw
)
Allocate resource
Child fibre running
Free resource
+Allocate resource
+Child fibre running
+Free resource
- : unit = ()
```
@ -414,8 +414,8 @@ We release when `fork_sub_ignore` fails due to parent switch being already off:
Switch.turn_off sw (Failure "Switch already off");
fork_sub_ignore_resource sw
)
Allocate resource
Free resource
+Allocate resource
+Free resource
Exception: Failure "Switch already off".
```
@ -427,8 +427,8 @@ We release when `fork_sub_ignore` fails due to parent switch being invalid:
Switch.sub sw ~on_error:raise (fun sub -> copy := sub);
fork_sub_ignore_resource !copy
)
Allocate resource
Free resource
+Allocate resource
+Free resource
Exception: Invalid_argument "Switch finished!".
```
@ -441,7 +441,7 @@ We release when `fork_sub_ignore`'s switch is turned off while running:
~on_release:(fun () -> traceln "Free resource")
(fun _sw -> failwith "Simulated error")
)
Allocate resource
Free resource
+Allocate resource
+Free resource
Exception: Failure "Simulated error".
```

View File

@ -31,11 +31,11 @@ Create a promise, fork a thread waiting for it, then fulfull it:
Fibre.yield ();
traceln "Thread after yield: %a" (pp_promise Fmt.string) thread;
traceln "Final result: %s" (Promise.await thread)
Initial state: unresolved
After being fulfilled: fulfilled:ok
Thread before yield: unresolved
Thread after yield: fulfilled:ok
Final result: ok
+Initial state: unresolved
+After being fulfilled: fulfilled:ok
+Thread before yield: unresolved
+Thread after yield: fulfilled:ok
+Final result: ok
```
Create a promise, fork a thread waiting for it, then break it:
@ -54,11 +54,11 @@ Create a promise, fork a thread waiting for it, then break it:
match Promise.await thread with
| x -> failwith x
| exception (Failure msg) -> traceln "Final result exception: %s" msg
Initial state: unresolved
After being broken: broken:test
Thread before yield: unresolved
Thread after yield: broken:test
Final result exception: test
+Initial state: unresolved
+After being broken: broken:test
+Thread before yield: unresolved
+Thread after yield: broken:test
+Final result exception: test
```
Some simple tests of `fork_ignore`:
@ -80,9 +80,9 @@ Some simple tests of `fork_ignore`:
assert false
with Exit ->
traceln "Forked code ran; i is now %d" !i
Forked code ran; i is now 1
Forked code waiting; i is still 1
Forked code ran; i is now 2
+Forked code ran; i is now 1
+Forked code waiting; i is still 1
+Forked code ran; i is now 2
```
Basic semaphore tests:
@ -116,9 +116,9 @@ Basic semaphore tests:
Semaphore.release sem;
decr running;
Semaphore.release sem
Semaphore means that only 2 threads are running
One finished; now 1 is running
Yield allows C to start; now 2 are running
+Semaphore means that only 2 threads are running
+One finished; now 1 is running
+Yield allows C to start; now 2 are running
```
Releasing a semaphore when no-one is waiting for it:
@ -137,8 +137,8 @@ Releasing a semaphore when no-one is waiting for it:
traceln "Now b running: %d" (Semaphore.get_value sem);
Semaphore.release sem; (* Release with an empty wait-queue *)
traceln "Finished: %d" (Semaphore.get_value sem)
Initial config: 1
A running: 0
Now b running: 0
Finished: 1
+Initial config: 1
+A running: 0
+Now b running: 0
+Finished: 1
```

View File

@ -68,9 +68,9 @@ Scheduling a timer that's already due:
Fibre.both ~sw
(fun () -> traceln "First fibre runs"; Eio.Time.sleep ~sw clock (-1.0); traceln "Sleep done")
(fun () -> traceln "Second fibre runs")
First fibre runs
Second fibre runs
Sleep done
+First fibre runs
+Second fibre runs
+Sleep done
- : unit = ()
```
@ -89,6 +89,6 @@ Check ordering works:
traceln "Short timer finished";
Switch.turn_off sw (Failure "Simulated cancel")
)
Short timer finished
+Short timer finished
Exception: Failure "Simulated cancel".
```

14
tests/test_trace.md Normal file
View File

@ -0,0 +1,14 @@
```ocaml
# #require "eio_main";;
# open Eio.Std;;
# Eio_main.run @@ fun _env ->
traceln "One-line trace";
traceln "@[<v2>A nested list@,Foo@,Bar@]";
traceln "Trace with position" ~__POS__:("test_trace.md", 5, 1, 10);
+One-line trace
+A nested list
+ Foo
+ Bar
+Trace with position [test_trace.md:5]
- : unit = ()
```