mirror of
https://github.com/c-cube/nanoev.git
synced 2025-12-06 03:05:32 -05:00
106 lines
3 KiB
OCaml
106 lines
3 KiB
OCaml
module Sem = Picos_std_sync.Semaphore.Counting
|
|
|
|
type client_handler = Unix.sockaddr -> IO_in.t -> IO_out.t -> unit
|
|
|
|
type t = {
|
|
active: bool Atomic.t;
|
|
sock: Unix.file_descr;
|
|
client_handler: client_handler;
|
|
spawn: (unit -> unit) -> unit Picos.Computation.t;
|
|
max_conns: int;
|
|
sem: Sem.t;
|
|
mutable running: unit Picos.Computation.t option;
|
|
exn_handler: exn -> Printexc.raw_backtrace -> unit;
|
|
}
|
|
|
|
let[@inline] join (self : t) : unit =
|
|
Option.iter Picos.Computation.await self.running
|
|
|
|
let[@inline] max_connections self = self.max_conns
|
|
|
|
let[@inline] n_active_connections (self : t) : int =
|
|
self.max_conns - Sem.get_value self.sem
|
|
|
|
let[@inline] running (self : t) : bool = Atomic.get self.active
|
|
let shutdown (self : t) = if Atomic.exchange self.active false then ()
|
|
|
|
open struct
|
|
let default_exn_handler exn bt =
|
|
Printf.eprintf "uncaught exception in network server: %s\n%s%!"
|
|
(Printexc.to_string exn)
|
|
(Printexc.raw_backtrace_to_string bt)
|
|
|
|
let run (self : t) () : unit =
|
|
while Atomic.get self.active do
|
|
let client_sock, client_addr = Base.accept self.sock in
|
|
Sem.acquire self.sem;
|
|
|
|
let cleanup () =
|
|
(try Unix.shutdown client_sock Unix.SHUTDOWN_ALL with _ -> ());
|
|
(* TODO: close in nanoev too *)
|
|
(try Unix.close client_sock with _ -> ());
|
|
Sem.release self.sem
|
|
in
|
|
|
|
let comp : _ Picos.Computation.t =
|
|
self.spawn (fun () ->
|
|
let ic = IO_in.of_unix_fd client_sock in
|
|
let oc = IO_out.of_unix_fd client_sock in
|
|
try
|
|
self.client_handler client_addr ic oc;
|
|
cleanup ()
|
|
with exn ->
|
|
let bt = Printexc.get_raw_backtrace () in
|
|
cleanup ();
|
|
self.exn_handler exn bt)
|
|
in
|
|
ignore (comp : _ Picos.Computation.t)
|
|
done
|
|
end
|
|
|
|
let establish ?backlog ?max_connections ?(exn_handler = default_exn_handler)
|
|
~spawn ~(client_handler : client_handler) addr : t =
|
|
let ev = Global_ev.get_nanoev_exn () in
|
|
let max_connections =
|
|
match max_connections with
|
|
| None -> Nanoev.max_fds ev
|
|
| Some n -> min (Nanoev.max_fds ev) n
|
|
in
|
|
let sem = Sem.make max_connections in
|
|
|
|
let backlog =
|
|
match backlog with
|
|
| Some n -> max 4 n
|
|
| None -> max 4 max_connections
|
|
in
|
|
|
|
let domain = Unix.domain_of_sockaddr addr in
|
|
let sock = Unix.socket domain Unix.SOCK_STREAM 0 in
|
|
|
|
Unix.bind sock addr;
|
|
Unix.listen sock backlog;
|
|
Unix.set_nonblock sock;
|
|
Unix.setsockopt sock Unix.SO_REUSEADDR true;
|
|
(try Unix.setsockopt sock Unix.TCP_NODELAY true with _ -> ());
|
|
|
|
let server =
|
|
{
|
|
active = Atomic.make true;
|
|
max_conns = max_connections;
|
|
sem;
|
|
spawn;
|
|
sock;
|
|
client_handler;
|
|
running = None;
|
|
exn_handler;
|
|
}
|
|
in
|
|
|
|
server.running <- Some (spawn (run server));
|
|
server
|
|
|
|
let with_ ?backlog ?max_connections ?exn_handler ~spawn ~client_handler addr f =
|
|
let server =
|
|
establish ?backlog ?max_connections ?exn_handler ~spawn ~client_handler addr
|
|
in
|
|
Fun.protect ~finally:(fun () -> shutdown server) (fun () -> f server)
|