mirror of
https://github.com/c-cube/tiny_httpd.git
synced 2025-12-06 11:15:35 -05:00
use lwt_direct from lwt PR
This commit is contained in:
parent
029c558802
commit
3014046a8a
6 changed files with 85 additions and 44 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
open Tiny_httpd_core
|
open Tiny_httpd_core
|
||||||
module Log = Tiny_httpd.Log
|
module Log = Tiny_httpd.Log
|
||||||
module MFD = Tiny_httpd_multipart_form_data
|
module MFD = Tiny_httpd_multipart_form_data
|
||||||
module Task = Tiny_httpd_lwt.Task
|
module Lwt_direct = Tiny_httpd_lwt.Lwt_direct
|
||||||
|
|
||||||
let now_ = Unix.gettimeofday
|
let now_ = Unix.gettimeofday
|
||||||
|
|
||||||
|
|
@ -151,11 +151,11 @@ let () =
|
||||||
let ev = new Lwt_engine.libev () in
|
let ev = new Lwt_engine.libev () in
|
||||||
Lwt_engine.set ev;
|
Lwt_engine.set ev;
|
||||||
|
|
||||||
Lwt_main.run @@ Task.run
|
Lwt_main.run @@ Lwt_direct.run
|
||||||
@@ fun () ->
|
@@ fun () ->
|
||||||
let server =
|
let server =
|
||||||
Tiny_httpd_lwt.create ~addr:!addr ~port:!port_ ~max_connections:!j ()
|
Tiny_httpd_lwt.create ~addr:!addr ~port:!port_ ~max_connections:!j ()
|
||||||
|> Task.await
|
|> Lwt_direct.await
|
||||||
in
|
in
|
||||||
|
|
||||||
Tiny_httpd_camlzip.setup ~compress_above:1024 ~buf_size:(16 * 1024) server;
|
Tiny_httpd_camlzip.setup ~compress_above:1024 ~buf_size:(16 * 1024) server;
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,16 @@
|
||||||
module ED = Effect.Deep
|
module ED = Effect.Deep
|
||||||
|
|
||||||
type _ Effect.t += Await : 'a Lwt.t -> 'a Effect.t
|
type _ Effect.t += Await : 'a Lwt.t -> 'a Effect.t | Yield : unit Effect.t
|
||||||
|
|
||||||
(** Queue of microtasks that are ready *)
|
(** Queue of microtasks that are ready *)
|
||||||
let tasks : (unit -> unit) Queue.t = Queue.create ()
|
let tasks : (unit -> unit) Queue.t = Queue.create ()
|
||||||
|
|
||||||
let[@inline] push_task f : unit = Queue.push f tasks
|
let[@inline] push_task f : unit = Queue.push f tasks
|
||||||
|
|
||||||
let on_uncaught_exn : (exn -> Printexc.raw_backtrace -> unit) ref =
|
let default_on_uncaught_exn exn bt =
|
||||||
ref (fun exn bt ->
|
Printf.eprintf "lwt_task: uncaught task exception:\n%s\n%s\n%!"
|
||||||
Printf.eprintf "lwt_task: uncaught task exception:\n%s\n%s\n%!"
|
(Printexc.to_string exn)
|
||||||
(Printexc.to_string exn)
|
(Printexc.raw_backtrace_to_string bt)
|
||||||
(Printexc.raw_backtrace_to_string bt))
|
|
||||||
|
|
||||||
let run_all_tasks () : unit =
|
let run_all_tasks () : unit =
|
||||||
let n_processed = ref 0 in
|
let n_processed = ref 0 in
|
||||||
|
|
@ -22,16 +21,21 @@ let run_all_tasks () : unit =
|
||||||
try t ()
|
try t ()
|
||||||
with exn ->
|
with exn ->
|
||||||
let bt = Printexc.get_raw_backtrace () in
|
let bt = Printexc.get_raw_backtrace () in
|
||||||
!on_uncaught_exn exn bt
|
default_on_uncaught_exn exn bt
|
||||||
done;
|
done;
|
||||||
(* make sure we don't sleep forever if there's no lwt promise
|
(* make sure we don't sleep forever if there's no lwt promise
|
||||||
ready but [tasks] contains ready tasks *)
|
ready but [tasks] contains ready tasks *)
|
||||||
if not (Queue.is_empty tasks) then ignore (Lwt.pause () : unit Lwt.t)
|
if not (Queue.is_empty tasks) then ignore (Lwt.pause () : unit Lwt.t)
|
||||||
|
|
||||||
let () =
|
let setup_hooks =
|
||||||
let _hook1 = Lwt_main.Enter_iter_hooks.add_first run_all_tasks in
|
let already_done = ref false in
|
||||||
let _hook2 = Lwt_main.Leave_iter_hooks.add_first run_all_tasks in
|
fun () ->
|
||||||
()
|
if not !already_done then (
|
||||||
|
already_done := true;
|
||||||
|
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 =
|
let await (fut : 'a Lwt.t) : 'a =
|
||||||
match Lwt.state fut with
|
match Lwt.state fut with
|
||||||
|
|
@ -39,10 +43,13 @@ let await (fut : 'a Lwt.t) : 'a =
|
||||||
| Lwt.Fail exn -> raise exn
|
| Lwt.Fail exn -> raise exn
|
||||||
| Lwt.Sleep -> Effect.perform (Await fut)
|
| Lwt.Sleep -> Effect.perform (Await fut)
|
||||||
|
|
||||||
|
let yield () : unit = Effect.perform Yield
|
||||||
|
|
||||||
(** the main effect handler *)
|
(** the main effect handler *)
|
||||||
let handler : _ ED.effect_handler =
|
let handler : _ ED.effect_handler =
|
||||||
let effc : type b. b Effect.t -> ((b, unit) ED.continuation -> 'a) option =
|
let effc : type b. b Effect.t -> ((b, unit) ED.continuation -> 'a) option =
|
||||||
function
|
function
|
||||||
|
| Yield -> Some (fun k -> push_task (fun () -> ED.continue k ()))
|
||||||
| Await fut ->
|
| Await fut ->
|
||||||
Some
|
Some
|
||||||
(fun k ->
|
(fun k ->
|
||||||
|
|
@ -51,10 +58,10 @@ let handler : _ ED.effect_handler =
|
||||||
(fun exn -> push_task (fun () -> ED.discontinue k exn)))
|
(fun exn -> push_task (fun () -> ED.discontinue k exn)))
|
||||||
| _ -> None
|
| _ -> None
|
||||||
in
|
in
|
||||||
|
|
||||||
{ effc }
|
{ effc }
|
||||||
|
|
||||||
let run_inside_effect_handler_ (type a) (promise : a Lwt.u) f () : unit =
|
let run_inside_effect_handler_and_resolve_ (type a) (promise : a Lwt.u) f () :
|
||||||
|
unit =
|
||||||
let res = ref (Error (Failure "not resolved")) in
|
let res = ref (Error (Failure "not resolved")) in
|
||||||
let run_f_and_set_res () =
|
let run_f_and_set_res () =
|
||||||
(try
|
(try
|
||||||
|
|
@ -66,10 +73,21 @@ let run_inside_effect_handler_ (type a) (promise : a Lwt.u) f () : unit =
|
||||||
ED.try_with run_f_and_set_res () handler
|
ED.try_with run_f_and_set_res () handler
|
||||||
|
|
||||||
let run f : _ Lwt.t =
|
let run f : _ Lwt.t =
|
||||||
|
setup_hooks ();
|
||||||
let lwt, resolve = Lwt.wait () in
|
let lwt, resolve = Lwt.wait () in
|
||||||
push_task (run_inside_effect_handler_ resolve f);
|
push_task (run_inside_effect_handler_and_resolve_ resolve f);
|
||||||
lwt
|
lwt
|
||||||
|
|
||||||
let run_async f : unit = ignore (run f : unit Lwt.t)
|
let run_inside_effect_handler_in_the_background_ ~on_uncaught_exn f () : unit =
|
||||||
|
let run_f () : unit =
|
||||||
|
try f ()
|
||||||
|
with exn ->
|
||||||
|
let bt = Printexc.get_raw_backtrace () in
|
||||||
|
on_uncaught_exn exn bt
|
||||||
|
in
|
||||||
|
ED.try_with run_f () handler
|
||||||
|
|
||||||
(* TODO: yield, use that in loops? *)
|
let run_in_the_background ?(on_uncaught_exn = default_on_uncaught_exn) f : unit
|
||||||
|
=
|
||||||
|
setup_hooks ();
|
||||||
|
push_task (run_inside_effect_handler_in_the_background_ ~on_uncaught_exn f)
|
||||||
33
src/lwt/lwt_direct.mli
Normal file
33
src/lwt/lwt_direct.mli
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
(** Direct style control flow for Lwt. *)
|
||||||
|
|
||||||
|
val run : (unit -> 'a) -> 'a Lwt.t
|
||||||
|
(** [run f] runs the function [f ()] in a task within
|
||||||
|
the [Lwt_unix] event loop. [f ()] can create [Lwt]
|
||||||
|
promises and use {!await} to wait for them. Like any promise
|
||||||
|
in Lwt, [f ()] can starve the event loop if it runs long computations
|
||||||
|
without yielding to the event loop.
|
||||||
|
|
||||||
|
When [f ()] terminates (successfully or not), the promise
|
||||||
|
[run f] is resolved with [f ()]'s result, or the exception
|
||||||
|
raised by [f ()]. *)
|
||||||
|
|
||||||
|
val run_in_the_background :
|
||||||
|
?on_uncaught_exn:(exn -> Printexc.raw_backtrace -> unit) ->
|
||||||
|
(unit -> unit) ->
|
||||||
|
unit
|
||||||
|
(** [run_in_the_background f] is similar to [ignore (run f)].
|
||||||
|
The computation [f()] runs in the background in the event loop
|
||||||
|
and returns no result.
|
||||||
|
@param on_uncaught_exn if provided, this is called when [f()]
|
||||||
|
raises an exception. *)
|
||||||
|
|
||||||
|
val yield : unit -> unit
|
||||||
|
(** Yield to the event loop.
|
||||||
|
Can only be used inside {!run} or {!run_in_the_background}. *)
|
||||||
|
|
||||||
|
val await : 'a Lwt.t -> 'a
|
||||||
|
(** [await prom] returns the result of [prom], or re-raises the
|
||||||
|
exception with which [prom] failed if it failed.
|
||||||
|
If [prom] is not resolved yet, [await prom] will suspend the
|
||||||
|
current task and resume it when [prom] is resolved.
|
||||||
|
Can only be used inside {!run} or {!run_in_the_background}. *)
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
(** Direct style tasks for Lwt *)
|
|
||||||
|
|
||||||
val run : (unit -> 'a) -> 'a Lwt.t
|
|
||||||
(** Run a microtask *)
|
|
||||||
|
|
||||||
val run_async : (unit -> unit) -> unit
|
|
||||||
|
|
||||||
val await : 'a Lwt.t -> 'a
|
|
||||||
(** Can only be used inside {!run} *)
|
|
||||||
|
|
@ -3,7 +3,7 @@ module H = Tiny_httpd.Server
|
||||||
module Pool = Tiny_httpd.Pool
|
module Pool = Tiny_httpd.Pool
|
||||||
module Slice = IO.Slice
|
module Slice = IO.Slice
|
||||||
module Log = Tiny_httpd.Log
|
module Log = Tiny_httpd.Log
|
||||||
module Task = Task
|
module Lwt_direct = Lwt_direct
|
||||||
|
|
||||||
let spf = Printf.sprintf
|
let spf = Printf.sprintf
|
||||||
let ( let@ ) = ( @@ )
|
let ( let@ ) = ( @@ )
|
||||||
|
|
@ -37,33 +37,33 @@ let ic_of_fd ~(num_open : int ref) ~bytes (fd : Lwt_unix.file_descr) :
|
||||||
assert (sl.len = 0);
|
assert (sl.len = 0);
|
||||||
sl.off <- 0;
|
sl.off <- 0;
|
||||||
let n =
|
let n =
|
||||||
Lwt_unix.read fd sl.bytes 0 (Bytes.length sl.bytes) |> Task.await
|
Lwt_unix.read fd sl.bytes 0 (Bytes.length sl.bytes) |> Lwt_direct.await
|
||||||
in
|
in
|
||||||
sl.len <- n
|
sl.len <- n
|
||||||
|
|
||||||
method close () =
|
method close () =
|
||||||
decr num_open;
|
decr num_open;
|
||||||
if !num_open <= 0 then Lwt_unix.close fd |> Task.await
|
if !num_open <= 0 then Lwt_unix.close fd |> Lwt_direct.await
|
||||||
end
|
end
|
||||||
|
|
||||||
let oc_of_fd ~(num_open : int ref) ~bytes (fd : Lwt_unix.file_descr) :
|
let oc_of_fd ~(num_open : int ref) ~bytes (fd : Lwt_unix.file_descr) :
|
||||||
IO.Output.t =
|
IO.Output.t =
|
||||||
object
|
object
|
||||||
inherit IO.Output.t_from_output ~bytes ()
|
inherit IO.Output.t_from_output ~bytes ()
|
||||||
(* method flush () : unit = Lwt_io.flush oc |> Task.await *)
|
(* method flush () : unit = Lwt_io.flush oc |> Lwt_direct.await *)
|
||||||
|
|
||||||
method private output_underlying buf i len =
|
method private output_underlying buf i len =
|
||||||
let i = ref i in
|
let i = ref i in
|
||||||
let len = ref len in
|
let len = ref len in
|
||||||
while !len > 0 do
|
while !len > 0 do
|
||||||
let n = Lwt_unix.write fd buf !i !len |> Task.await in
|
let n = Lwt_unix.write fd buf !i !len |> Lwt_direct.await in
|
||||||
i := !i + n;
|
i := !i + n;
|
||||||
len := !len - n
|
len := !len - n
|
||||||
done
|
done
|
||||||
|
|
||||||
method private close_underlying () =
|
method private close_underlying () =
|
||||||
decr num_open;
|
decr num_open;
|
||||||
if !num_open <= 0 then Lwt_unix.close fd |> Task.await
|
if !num_open <= 0 then Lwt_unix.close fd |> Lwt_direct.await
|
||||||
end
|
end
|
||||||
|
|
||||||
let io_backend ?addr ?port ?unix_sock ?max_connections ?max_buf_pool_size
|
let io_backend ?addr ?port ?unix_sock ?max_connections ?max_buf_pool_size
|
||||||
|
|
@ -80,7 +80,7 @@ let io_backend ?addr ?port ?unix_sock ?max_connections ?max_buf_pool_size
|
||||||
| addr, port, None ->
|
| addr, port, None ->
|
||||||
let addr = Option.value ~default:"127.0.0.1" addr in
|
let addr = Option.value ~default:"127.0.0.1" addr in
|
||||||
let sockaddr, port =
|
let sockaddr, port =
|
||||||
match Lwt_unix.getaddrinfo addr "" [] |> Task.await, port with
|
match Lwt_unix.getaddrinfo addr "" [] |> Lwt_direct.await, port with
|
||||||
| { Unix.ai_addr = ADDR_INET (h, _); _ } :: _, None ->
|
| { Unix.ai_addr = ADDR_INET (h, _); _ } :: _, None ->
|
||||||
let p = 8080 in
|
let p = 8080 in
|
||||||
Unix.ADDR_INET (h, p), p
|
Unix.ADDR_INET (h, p), p
|
||||||
|
|
@ -115,7 +115,7 @@ let io_backend ?addr ?port ?unix_sock ?max_connections ?max_buf_pool_size
|
||||||
let port = ref port in
|
let port = ref port in
|
||||||
|
|
||||||
let server_loop : unit Lwt.t =
|
let server_loop : unit Lwt.t =
|
||||||
let@ () = Task.run in
|
let@ () = Lwt_direct.run in
|
||||||
let backlog = max_connections in
|
let backlog = max_connections in
|
||||||
let sock =
|
let sock =
|
||||||
Lwt_unix.socket ~cloexec:true
|
Lwt_unix.socket ~cloexec:true
|
||||||
|
|
@ -126,7 +126,7 @@ let io_backend ?addr ?port ?unix_sock ?max_connections ?max_buf_pool_size
|
||||||
Lwt_unix.setsockopt_optint sock Unix.SO_LINGER None;
|
Lwt_unix.setsockopt_optint sock Unix.SO_LINGER None;
|
||||||
Lwt_unix.setsockopt sock Unix.SO_REUSEADDR true;
|
Lwt_unix.setsockopt sock Unix.SO_REUSEADDR true;
|
||||||
Lwt_unix.setsockopt sock Unix.SO_REUSEPORT true;
|
Lwt_unix.setsockopt sock Unix.SO_REUSEPORT true;
|
||||||
Lwt_unix.bind sock sockaddr |> Task.await;
|
Lwt_unix.bind sock sockaddr |> Lwt_direct.await;
|
||||||
Lwt_unix.listen sock backlog;
|
Lwt_unix.listen sock backlog;
|
||||||
|
|
||||||
(* recover real port, if any *)
|
(* recover real port, if any *)
|
||||||
|
|
@ -136,8 +136,7 @@ let io_backend ?addr ?port ?unix_sock ?max_connections ?max_buf_pool_size
|
||||||
|
|
||||||
let handle_client client_addr fd : unit =
|
let handle_client client_addr fd : unit =
|
||||||
Atomic.incr active_conns;
|
Atomic.incr active_conns;
|
||||||
let@ () = Task.run_async in
|
Lwt_direct.run_in_the_background @@ fun () ->
|
||||||
|
|
||||||
let cleanup () =
|
let cleanup () =
|
||||||
Log.debug (fun k ->
|
Log.debug (fun k ->
|
||||||
k "Tiny_httpd_lwt: client handler returned");
|
k "Tiny_httpd_lwt: client handler returned");
|
||||||
|
|
@ -169,7 +168,7 @@ let io_backend ?addr ?port ?unix_sock ?max_connections ?max_buf_pool_size
|
||||||
in
|
in
|
||||||
|
|
||||||
while Atomic.get running do
|
while Atomic.get running do
|
||||||
let fd, addr = Lwt_unix.accept sock |> Task.await in
|
let fd, addr = Lwt_unix.accept sock |> Lwt_direct.await in
|
||||||
handle_client addr fd
|
handle_client addr fd
|
||||||
done
|
done
|
||||||
in
|
in
|
||||||
|
|
@ -181,21 +180,21 @@ let io_backend ?addr ?port ?unix_sock ?max_connections ?max_buf_pool_size
|
||||||
(fun () ->
|
(fun () ->
|
||||||
Atomic.set running false;
|
Atomic.set running false;
|
||||||
Lwt.wakeup_later set_server_done ();
|
Lwt.wakeup_later set_server_done ();
|
||||||
Task.await server_loop);
|
Lwt_direct.await server_loop);
|
||||||
endpoint = (fun () -> addr, !port);
|
endpoint = (fun () -> addr, !port);
|
||||||
active_connections = (fun () -> Atomic.get active_conns);
|
active_connections = (fun () -> Atomic.get active_conns);
|
||||||
}
|
}
|
||||||
in
|
in
|
||||||
|
|
||||||
after_init tcp_server;
|
after_init tcp_server;
|
||||||
Task.await server_done);
|
Lwt_direct.await server_done);
|
||||||
}
|
}
|
||||||
end in
|
end in
|
||||||
(module M)
|
(module M)
|
||||||
|
|
||||||
let create ?addr ?port ?unix_sock ?max_connections ?max_buf_pool_size ?buf_size
|
let create ?addr ?port ?unix_sock ?max_connections ?max_buf_pool_size ?buf_size
|
||||||
?middlewares () : H.t Lwt.t =
|
?middlewares () : H.t Lwt.t =
|
||||||
let@ () = Task.run in
|
let@ () = Lwt_direct.run in
|
||||||
let backend =
|
let backend =
|
||||||
io_backend ?addr ?port ?unix_sock ?max_buf_pool_size ?max_connections
|
io_backend ?addr ?port ?unix_sock ?max_buf_pool_size ?max_connections
|
||||||
?buf_size ()
|
?buf_size ()
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
{b NOTE}: this is very experimental and will absolutely change over time,
|
{b NOTE}: this is very experimental and will absolutely change over time,
|
||||||
@since NEXT_RELEASE *)
|
@since NEXT_RELEASE *)
|
||||||
|
|
||||||
module Task = Task
|
module Lwt_direct = Lwt_direct
|
||||||
|
|
||||||
type 'a with_args =
|
type 'a with_args =
|
||||||
?addr:string ->
|
?addr:string ->
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue