문서의 deep 핸들러 예제에는 이런 문단이 있어요:
계산 f 자체도 효과를 수행할 수 있습니다. f가 Yield를 수행하면 현재 작업을 멈추고 실행 가능한 작업 큐에 넣은 뒤, 스케줄러가 다음 작업을 실행합니다. 효과가 Fork f라면 현재 작업을 멈추고 spawn f를 꼬리 호출해 새 작업 f를 곧바로 실행합니다. 새 작업을 먼저 실행하기로 한 건 임의의 선택이라는 점을 보세요. 다른 선택으로는 f의 작업을 준비 큐에 넣고 k를 즉시 재개할 수도 있습니다.
제가 뭔가 간단한 걸 놓쳤을 수도 있지만, 이 스케줄러 예제를 보면요:
type _ Effect.t += Fork : (unit -> unit) -> unit t
| Yield : unit t
(* Round-robin scheduling with concurrency *)
let run (entry : unit -> unit) : unit =
let pending : (int * (int, unit) continuation) option ref =
ref None (* exchanger awaiting a partner *)
in
let ready = Queue.create () in (* ready-task queue *)
let schedule cont arg =
let resume () = continue cont arg in
Queue.push
resume ready
in
let next () =
if Queue.is_empty ready then () (* finished *)
else begin
let resume = Queue.pop ready in
resume ()
end
in
let rec start (work : unit -> unit) : unit =
match work () with
| () -> next ()
| exception err ->
print_endline (Printexc.to_string err);
next ()
| effect Yield, cont -> schedule cont (); next ()
(* How can work be queued at this point? *)
| effect (Fork work), cont -> schedule cont (); start work
| effect (Xchg value), cont ->
begin match !pending with
| Some (other, saved) -> pending := None; schedule saved value; continue cont other
| None -> pending := Some (value, cont); next ()
end
in
start entry
work를 ready에 어떻게 넣나요?
영어에서 번역·원문 보기In the documentation's deep-handler example, there's this paragraph:
A computation f can itself perform effects. When f performs Yield, its task is paused and placed in the ready-task queue, then the scheduler runs the next queued task. When the effect is Fork f, it pauses the current task and immediately runs the new task f by tail-calling spawn f. Note that picking the new task to run first is an arbitrary decision. Another possible choice would put f's task into the ready queue and immediately continue k instead.
Perhaps I'm overlooking something simple, but consider this scheduler example:
type _ Effect.t += Fork : (unit -> unit) -> unit t
| Yield : unit t
(* Round-robin scheduling with concurrency *)
let run (entry : unit -> unit) : unit =
let pending : (int * (int, unit) continuation) option ref =
ref None (* exchanger awaiting a partner *)
in
let ready = Queue.create () in (* ready-task queue *)
let schedule cont arg =
let resume () = continue cont arg in
Queue.push
resume ready
in
let next () =
if Queue.is_empty ready then () (* finished *)
else begin
let resume = Queue.pop ready in
resume ()
end
in
let rec start (work : unit -> unit) : unit =
match work () with
| () -> next ()
| exception err ->
print_endline (Printexc.to_string err);
next ()
| effect Yield, cont -> schedule cont (); next ()
(* How can work be queued at this point? *)
| effect (Fork work), cont -> schedule cont (); start work
| effect (Xchg value), cont ->
begin match !pending with
| Some (other, saved) -> pending := None; schedule saved value; continue cont other
| None -> pending := Some (value, cont); next ()
end
in
start entry
What would put work onto ready?