The example for deep handlers in the docs has this following paragraph:
The computation f may also perform effects. If f performs the Yield effect, the current task is suspended (inserted into the queue of ready tasks), and the next task from the scheduler queue is run. If the effect is Fork f, then the current task is suspended, and the new task f is executed immediately via a tail call to spawn f. Note that this choice to run the new task first is arbitrary. We could very well have chosen instead to insert the task for f into the ready queue and resumed k immediately.
Maybe I am missing something obvious, but given this scheduler example:
type _ Effect.t += Fork : (unit -> unit) -> unit t
| Yield : unit t
(* A concurrent round-robin scheduler *)
let run (main : unit -> unit) : unit =
let exchanger : (int * (int, unit) continuation) option ref =
ref None (* waiting exchanger *)
in
let run_q = Queue.create () in (* scheduler queue *)
let enqueue k v =
let task () = continue k v in
Queue.push task run_q
in
let dequeue () =
if Queue.is_empty run_q then () (* done *)
else begin
let task = Queue.pop run_q in
task ()
end
in
let rec spawn (f : unit -> unit) : unit =
match f () with
| () -> dequeue ()
| exception e ->
print_endline (Printexc.to_string e);
dequeue ()
| effect Yield, k -> enqueue k (); dequeue ()
(* How to enqueue f into the queue here? *)
| effect (Fork f), k -> enqueue k (); spawn f
| effect (Xchg n), k ->
begin match !exchanger with
| Some (n', k') -> exchanger := None; enqueue k' n; continue k n'
| None -> exchanger := Some (n, k); dequeue ()
end
in
spawn main
How would one enqueue f into run_q?