Spawning a fiber will disable effect handlers installed after main loop

I have been working on a toy example that better demonstrates the problem with OCaml’s current effect handling system:

open Effect
open Effect.Deep
open Eio

type _ Effect.t += Log : string -> unit t

(* The following function is not aware of any fibers. *)
let logging_with_prefix prefix f =
  try_with f ()
    {
      effc =
        (fun (type a) (eff : a t) ->
          match eff with
          | Log message ->
              Some
                (fun (k : (a, _) continuation) ->
                  print_string prefix;
                  print_endline message;
                  continue k ())
          | _ -> None);
    }

(* The following function is not aware of any fibers. *)
let logging = logging_with_prefix "LOG: "

(* The following function is not aware of any fibers. *)
let logging_important = logging_with_prefix "LOG IMPORTANT: "

(* The following function is generic and not aware of any effects. *)
let do_twice_parallel f = Fiber.both f f

(* The following function is performing but not handling any effects. *)
let double_hello () = do_twice_parallel (fun () -> perform (Log "Hello World!"))

let foo () =
  Fiber.both
    (fun () ->
      (* This should run concurrently with fibers spawned outside [foo]. *)
      for i = 1 to 5 do
        perform (Log ("tick " ^ string_of_int i));
        Fiber.yield ()
      done)
    (fun () ->
      (* We only want this to be logged specially. *)
      logging_important double_hello)

let () =
  logging (fun () ->
      Eio_main.run (fun _env ->
          Fiber.both foo (fun () ->
              for i = 1 to 5 do
                perform (Log ("tock " ^ string_of_int i));
                Fiber.yield ()
              done)))

This creates the following output:

LOG: tick 1                       
LOG: Hello World!
LOG: Hello World!
LOG: tock 1
LOG: tick 2
LOG: tock 2
LOG: tick 3
LOG: tock 3
LOG: tick 4
LOG: tock 4
LOG: tick 5
LOG: tock 5

Now, how to fix the above example to write

LOG: tick 1
LOG IMPORTANT: Hello World!
LOG IMPORTANT: Hello World!
LOG: tock 1
LOG: tick 2
…

instead, without changing the function double_hello and while further keeping do_twice_parallel generic (edit: and without tapping into Eio’s private effects)? I don’t think that’s possible, is it?

I was able to solve this in Lua by making the effect handling be aware of the fibers. See this Lua code, which, using this version of the Lua effect/fiber library, “correctly” prints “LOG IMPORTANT” in front of “Hello World!”:

LOG: tick 1
LOG IMPORTANT: Hello World!
LOG IMPORTANT: Hello World!
LOG: tick 2
LOG: tick 3
LOG: tock 1
LOG: tick 4
LOG: tock 2
LOG: tick 5
LOG: tock 3
LOG: tock 4
LOG: tock 5

(The order of the tick’s and tock’s is a bit off, but that’s not the point here.)


Update:

I opened this issue for tracking the problem: