Since the release of ocaml 5.0 is imminent, I have finally been looking into the new multicore features and how I can use them in my code.
There is one thing that I don’t understand about the interaction of Effect.try_with
and spawn
and join
from Domain
: if I spawn
a function that performs an effect, the corresponding handler in try_with
is not invoked when I wrap the try_with
around join
or the combination of spawn
and join
. Instead, the exception Unhandled
is raised. I if put the try_with
inside of spawn
, everything works - but that’s the boring version.
An exception that is raised inside of spawn
can be caught by wrapping join
in try ... with
.
My question is now: is this the intended behaviour (thou shallst not handle effects across domain boundaries) or is it a bug in 4.12.0+domains
and 5.00.0
?
open EffectHandlers (* will be Effect with 5.0 *)
open Deep
exception Foo
type _ eff += Square : int -> int eff
open Printf
let raise_foo () =
raise Foo
let perform_square n () =
printf "perform (Square %d).\n" n;
let n2 = perform (Square n) in
printf "continuing with Square %d -> %d.\n" n n2
let handle_square =
fun (type a) (e : a eff) ->
match e with
| Square n ->
Some
(fun (k : (a, _) continuation) ->
printf "handling (Square %d)\n" n;
continue k (n * n))
| _ -> None
let perform_square_handled n () =
try_with
(perform_square n) ()
{ effc = handle_square }
let handle_inside () =
let domain = Domain.spawn (perform_square_handled 42) in
printf "joining domain ...\n";
Domain.join domain;
printf "done.\n"
let handle_around_join () =
let domain = Domain.spawn (perform_square 42) in
printf "joining domain ...\n";
try_with
(fun () ->
Domain.join domain;
printf "done.\n")
()
{ effc = handle_square }
let handle_outside () =
try_with
(fun () ->
let domain = Domain.spawn (perform_square 42) in
printf "joining domain ...\n";
Domain.join domain;
printf "done.\n")
()
{ effc = handle_square }
let catch_inside () =
let domain = Domain.spawn raise_foo in
printf "joining domain ...\n";
match Domain.join domain with
| () -> printf "done.\n"
| exception Foo -> printf "caught exception in Domain.join.\n"
let catch_outside () =
try
let domain = Domain.spawn raise_foo in
printf "joining domain ...\n";
Domain.join domain;
printf "done.\n"
with
| Foo -> Printf.printf "caught exception outside.\n"
type mode =
| Handle_inside
| Handle_outside
| Handle_around_join
| Catch_inside
| Catch_outside
let _ =
let mode = ref Handle_inside in
let usage =
"usage: " ^ my_name ^ " ..." in
let options =
Arg.align
[ ("-handle_inside", Arg.Unit (fun () -> mode := Handle_inside),
" handle effect inside of Domain.spawn");
("-handle_outside", Arg.Unit (fun () -> mode := Handle_outside),
" handle effect outside of Domain.spawn");
("-handle_around_join", Arg.Unit (fun () -> mode := Handle_around_join),
" handle effect outside of Domain.spawn");
("-catch_inside", Arg.Unit (fun () -> mode := Catch_inside),
" catch the exception around Domain.join");
("-catch_outside", Arg.Unit (fun () -> mode := Catch_outside),
" catch the exception around Domain.spawn/join") ] in
Arg.parse options (fun s -> raise (Arg.Bad ("invalid argument: " ^ s))) usage;
match !mode with
| Handle_inside -> handle_inside ()
| Handle_outside -> handle_outside ()
| Handle_around_join -> handle_around_join ()
| Catch_inside -> catch_inside ()
| Catch_outside -> catch_outside ()