Allow returning a result from Eio_main.run

This is useful for e.g. cmdliner. We want to parse the command line
arguments inside the event loop so that we can use Eio, but we want to
call `exit` with the exit code after the loop has cleaned up.
This commit is contained in:
Thomas Leonard 2022-07-25 19:35:19 +01:00
parent 338d74bc8c
commit a9620bcf3d
6 changed files with 21 additions and 14 deletions

View File

@ -41,7 +41,7 @@ Before we start, we'll define a wrapper around `Eio_main.run` for the examples b
Eio_main.run @@ fun env ->
let domain_mgr = Eio.Stdenv.domain_mgr env in
fn (Eio.Domain_manager.run domain_mgr);;
val run : (((unit -> 'a) -> 'a) -> unit) -> unit = <fun>
val run : (((unit -> 'a) -> 'a) -> 'b) -> 'b = <fun>
```
## Problems with Multicore Programming

View File

@ -1222,7 +1222,8 @@ let monitor_event_fd t =
assert (got = 8);
(* We just go back to sleep now, but this will cause the scheduler to look
at the run queue again and notice any new items. *)
done
done;
assert false
let no_fallback (`Msg msg) = failwith msg
@ -1241,7 +1242,9 @@ let with_uring ~queue_depth ?polling_timeout ?(fallback=no_fallback) fn =
end;
Printexc.raise_with_backtrace ex bt
let rec run ?(queue_depth=64) ?n_blocks ?(block_size=4096) ?polling_timeout ?fallback main =
let rec run : type a.
?queue_depth:int -> ?n_blocks:int -> ?block_size:int -> ?polling_timeout:int -> ?fallback:(_ -> a) -> (_ -> a) -> a =
fun ?(queue_depth=64) ?n_blocks ?(block_size=4096) ?polling_timeout ?fallback main ->
Log.debug (fun l -> l "starting run");
let n_blocks = Option.value n_blocks ~default:queue_depth in
let stdenv = stdenv ~run_event_loop:(run ~queue_depth ~n_blocks ~block_size ?polling_timeout ?fallback:None) in
@ -1383,6 +1386,7 @@ let rec run ?(queue_depth=64) ?n_blocks ?(block_size=4096) ?polling_timeout ?fal
| _ -> None
}
in
let result = ref None in
let `Exit_scheduler =
let new_fiber = Fiber_context.make_root () in
fork ~new_fiber (fun () ->
@ -1396,10 +1400,13 @@ let rec run ?(queue_depth=64) ?n_blocks ?(block_size=4096) ?polling_timeout ?fal
Unix.close fd
);
Log.debug (fun f -> f "Monitoring eventfd %a" FD.pp st.eventfd);
Fiber.first
(fun () -> main stdenv)
(fun () -> monitor_event_fd st)
result := Some (
Fiber.first
(fun () -> main stdenv)
(fun () -> monitor_event_fd st)
)
)
)
in
Log.debug (fun l -> l "exit")
Log.debug (fun l -> l "exit");
Option.get !result

View File

@ -84,8 +84,8 @@ val run :
?n_blocks:int ->
?block_size:int ->
?polling_timeout:int ->
?fallback:([`Msg of string] -> unit) ->
(stdenv -> unit) -> unit
?fallback:([`Msg of string] -> 'a) ->
(stdenv -> 'a) -> 'a
(** Run an event loop using io_uring.
Uses {!Uring.create} to create the io_uring,

View File

@ -813,7 +813,7 @@ let rec wakeup ~async run_q =
Luv.Async.send async |> or_raise
| None -> ()
let rec run main =
let rec run : type a. (_ -> a) -> a = fun main ->
Log.debug (fun l -> l "starting run");
let loop = Luv.Loop.init () |> or_raise in
let run_q = Lf_queue.create () in
@ -908,7 +908,7 @@ let rec run main =
let new_fiber = Fiber_context.make_root () in
fork ~new_fiber (fun () ->
begin match main stdenv with
| () -> main_status := `Done
| v -> main_status := `Done v
| exception ex -> main_status := `Ex (ex, Printexc.get_raw_backtrace ())
end;
Luv.Loop.stop loop
@ -917,6 +917,6 @@ let rec run main =
Lf_queue.close st.run_q;
Luv.Handle.close async (fun () -> Luv.Loop.close loop |> or_raise);
match !main_status with
| `Done -> ()
| `Done v -> v
| `Ex (ex, bt) -> Printexc.raise_with_backtrace ex bt
| `Running -> failwith "Deadlock detected: no events scheduled but main function hasn't returned"

View File

@ -122,4 +122,4 @@ val get_fd_opt : #Eio.Generic.t -> Low_level.File.t option
(** {1 Main Loop} *)
val run : (stdenv -> unit) -> unit
val run : (stdenv -> 'a) -> 'a

View File

@ -1,6 +1,6 @@
(** Select a suitable event loop for Eio. *)
val run : (Eio.Stdenv.t -> unit) -> unit
val run : (Eio.Stdenv.t -> 'a) -> 'a
(** [run fn] runs an event loop and then calls [fn env] within it.
[env] provides access to the process's environment (file-system, network, etc).