mirror of
https://github.com/c-cube/tiny_httpd.git
synced 2025-12-07 03:35:34 -05:00
76 lines
2.2 KiB
OCaml
76 lines
2.2 KiB
OCaml
module ED = Effect.Deep
|
|
|
|
type _ Effect.t += Await : 'a Lwt.t -> 'a Effect.t
|
|
|
|
(** Queue of microtasks that are ready *)
|
|
let tasks : (unit -> unit) Queue.t = Queue.create ()
|
|
|
|
let[@inline] push_task f : unit = Queue.push f tasks
|
|
|
|
let on_uncaught_exn : (exn -> Printexc.raw_backtrace -> unit) ref =
|
|
ref (fun exn bt ->
|
|
Printf.eprintf "lwt_task: uncaught task exception:\n%s\n%s\n%!"
|
|
(Printexc.to_string exn)
|
|
(Printexc.raw_backtrace_to_string bt))
|
|
|
|
let run_all_tasks () : unit =
|
|
(* use local queue to prevent the hook from running forever in case
|
|
tasks keep scheduling new tasks. *)
|
|
let local = Queue.create () in
|
|
Queue.transfer tasks local;
|
|
while not (Queue.is_empty local) do
|
|
let t = Queue.pop local in
|
|
try t ()
|
|
with exn ->
|
|
let bt = Printexc.get_raw_backtrace () in
|
|
!on_uncaught_exn exn bt
|
|
done;
|
|
(* make sure we don't sleep forever if there's no lwt promise
|
|
ready but [tasks] contains ready tasks *)
|
|
if not (Queue.is_empty tasks) then ignore (Lwt.pause () : unit Lwt.t)
|
|
|
|
let () =
|
|
let _hook1 = Lwt_main.Enter_iter_hooks.add_first run_all_tasks in
|
|
let _hook2 = Lwt_main.Leave_iter_hooks.add_first run_all_tasks in
|
|
()
|
|
|
|
let await (fut : 'a Lwt.t) : 'a =
|
|
match Lwt.state fut with
|
|
| Lwt.Return x -> x
|
|
| Lwt.Fail exn -> raise exn
|
|
| Lwt.Sleep -> Effect.perform (Await fut)
|
|
|
|
(** the main effect handler *)
|
|
let handler : _ ED.effect_handler =
|
|
let effc : type b. b Effect.t -> ((b, unit) ED.continuation -> 'a) option =
|
|
function
|
|
| Await fut ->
|
|
Some
|
|
(fun k ->
|
|
Lwt.on_any fut
|
|
(fun res -> push_task (fun () -> ED.continue k res))
|
|
(fun exn -> push_task (fun () -> ED.discontinue k exn)))
|
|
| _ -> None
|
|
in
|
|
|
|
{ effc }
|
|
|
|
let run_inside_effect_handler_ (type a) (promise : a Lwt.u) f () : unit =
|
|
let res = ref (Error (Failure "not resolved")) in
|
|
let run_f_and_set_res () =
|
|
(try
|
|
let r = f () in
|
|
res := Ok r
|
|
with exn -> res := Error exn);
|
|
Lwt.wakeup_result promise !res
|
|
in
|
|
ED.try_with run_f_and_set_res () handler
|
|
|
|
let run f : _ Lwt.t =
|
|
let lwt, resolve = Lwt.wait () in
|
|
push_task (run_inside_effect_handler_ resolve f);
|
|
lwt
|
|
|
|
let run_async f : unit = ignore (run f : unit Lwt.t)
|
|
|
|
(* TODO: yield, use that in loops? *)
|