test.md
June 13, 2022 ยท View on GitHub
Setup
# #require "eio_main";;
# #require "lwt_eio";;
open Lwt.Syntax
open Eio.Std
let run fn =
Eio_main.run @@ fun env ->
Lwt_eio.with_event_loop ~clock:env#clock @@ fun _ ->
fn ()
Fairness
Lwt and Eio fibers don't block each other, although Lwt_main.run does call Lwt.wakeup_paused twice
during each iteration of the main loop, so the Lwt thread runs twice as often.
# run @@ fun () ->
Fiber.both
(fun () ->
for i = 1 to 8 do
traceln "eio: i = %d" i;
Fiber.yield ()
done
)
(fun () ->
Lwt_eio.Promise.await_lwt begin
let rec aux i =
traceln " lwt: i = %d" i;
let* () = Lwt.pause () in
if i < 8 then aux (i + 1)
else Lwt.return_unit
in
aux 1
end
);;
+eio: i = 1
+ lwt: i = 1
+eio: i = 2
+ lwt: i = 2
+ lwt: i = 3
+eio: i = 3
+ lwt: i = 4
+ lwt: i = 5
+eio: i = 4
+ lwt: i = 6
+ lwt: i = 7
+eio: i = 5
+ lwt: i = 8
+eio: i = 6
+eio: i = 7
+eio: i = 8
- : unit = ()
Forking
We can fork (as long we we're not using multiple domains):
# run @@ fun () ->
let output = Lwt_eio.run_lwt (fun () -> Lwt_process.(pread (shell "echo test"))) in
traceln "Subprocess produced %S" output;;
+Subprocess produced "test\n"
- : unit = ()
Signals
Installing a signal handler with Lwt_unix.on_signal causes lwt_unix_send_notification to be called when the signal occurs. This signals the main event thread by writing to a pipe or eventfd, which is processed by Eio in the usual way.
# run @@ fun () ->
let p, r = Promise.create () in
Lwt_eio.run_lwt (fun () ->
let handler = Lwt_unix.on_signal Sys.sigusr1 (fun (_ : int) ->
traceln "Signal handler received USR1";
(* It's OK to use Promise.resolve here, as it never blocks *)
Promise.resolve r ()
)
in
Unix.(kill (getpid ())) Sys.sigusr1;
let* () = Lwt_eio.Promise.await_eio p in
traceln "Finished; removing signal handler";
Lwt_unix.disable_signal_handler handler;
Lwt.return_unit
);;
+Signal handler received USR1
+Finished; removing signal handler
- : unit = ()
Exceptions
Lwt failures become Eio exceptions:
# run @@ fun () ->
try Lwt_eio.run_lwt (fun () -> Lwt.fail_with "Simulated error")
with Failure msg -> traceln "Eio caught: %s" msg;;
+Eio caught: Simulated error
- : unit = ()
Eio exceptions become Lwt failures:
# run @@ fun () ->
Lwt_eio.run_lwt (fun () ->
Lwt.catch
(fun () -> Lwt_eio.run_eio (fun () -> failwith "Simulated error"))
(fun ex -> traceln "Lwt caught: %a" Fmt.exn ex; Lwt.return_unit)
);;
+Lwt caught: Failure("Simulated error")
- : unit = ()
Cancellation
Cancelling an Eio fiber that is running Lwt code cancels the Lwt promise:
# run @@ fun () ->
let p, r = Lwt.task () in
try
Fiber.both
(fun () -> Lwt_eio.run_lwt (fun () -> p))
(fun () -> failwith "Simulated error");
with Failure _ ->
traceln "Checking status of Lwt promise...";
Lwt_eio.Promise.await_lwt p;;
+Checking status of Lwt promise...
Exception: Lwt.Resolution_loop.Canceled.
Cancelling a Lwt thread that is running Eio code cancels the Eio context:
# run @@ fun () ->
Switch.run @@ fun sw ->
Lwt_eio.run_lwt (fun () ->
let p = Lwt_eio.run_eio (fun () ->
try Fiber.await_cancel ()
with Eio.Cancel.Cancelled ex -> traceln "Eio fiber cancelled: %a" Fmt.exn ex
) in
Lwt.cancel p;
Lwt.return_unit
);;
+Eio fiber cancelled: Lwt.Resolution_loop.Canceled
- : unit = ()
Trying to run Lwt code from an already-cancelled context fails immediately:
# run @@ fun () ->
Switch.run @@ fun sw ->
Switch.fail sw (Failure "Simulated error");
Lwt_eio.run_lwt (fun () ->
let* () = Lwt.pause () in
assert false
);;
Exception: Failure "Simulated error".