mirror of
https://github.com/c-cube/moonpool.git
synced 2025-12-05 19:00:33 -05:00
feat: move to poll for IOs
This commit is contained in:
parent
b208506841
commit
a147eeb0ab
3 changed files with 84 additions and 61 deletions
|
|
@ -17,7 +17,7 @@ let rec read (fd : Fd.t) buf i len : int =
|
||||||
{
|
{
|
||||||
handle =
|
handle =
|
||||||
(fun ~run:_ ~resume sus ->
|
(fun ~run:_ ~resume sus ->
|
||||||
Ev_loop.wait_readable fd.fd cancel (fun cancel ->
|
Ev_loop.wait_readable fd cancel (fun cancel ->
|
||||||
resume sus @@ Ok ();
|
resume sus @@ Ok ();
|
||||||
Cancel_handle.cancel cancel));
|
Cancel_handle.cancel cancel));
|
||||||
};
|
};
|
||||||
|
|
@ -40,7 +40,7 @@ let rec write_once (fd : Fd.t) buf i len : int =
|
||||||
{
|
{
|
||||||
handle =
|
handle =
|
||||||
(fun ~run:_ ~resume sus ->
|
(fun ~run:_ ~resume sus ->
|
||||||
Ev_loop.wait_writable fd.fd cancel (fun cancel ->
|
Ev_loop.wait_writable fd cancel (fun cancel ->
|
||||||
resume sus @@ Ok ();
|
resume sus @@ Ok ();
|
||||||
Cancel_handle.cancel cancel));
|
Cancel_handle.cancel cancel));
|
||||||
};
|
};
|
||||||
|
|
@ -159,8 +159,8 @@ module TCP_server = struct
|
||||||
| Running
|
| Running
|
||||||
| Stopped
|
| Stopped
|
||||||
|
|
||||||
let rec accept_ (sock : Unix.file_descr) =
|
let rec accept_ (sock : Fd.t) =
|
||||||
match Unix.accept sock with
|
match Unix.accept sock.fd with
|
||||||
| csock, addr -> csock, addr
|
| csock, addr -> csock, addr
|
||||||
| exception Unix.Unix_error ((Unix.EAGAIN | Unix.EWOULDBLOCK), _, _) ->
|
| exception Unix.Unix_error ((Unix.EAGAIN | Unix.EWOULDBLOCK), _, _) ->
|
||||||
(let cancel = Cancel_handle.create () in
|
(let cancel = Cancel_handle.create () in
|
||||||
|
|
@ -219,7 +219,7 @@ module TCP_server = struct
|
||||||
Unix.set_nonblock sock;
|
Unix.set_nonblock sock;
|
||||||
Unix.bind sock addr;
|
Unix.bind sock addr;
|
||||||
Unix.listen sock listen;
|
Unix.listen sock listen;
|
||||||
sock
|
Fd.create sock
|
||||||
with e ->
|
with e ->
|
||||||
let bt = Printexc.get_raw_backtrace () in
|
let bt = Printexc.get_raw_backtrace () in
|
||||||
A.set st Stopped;
|
A.set st Stopped;
|
||||||
|
|
@ -273,8 +273,8 @@ end
|
||||||
|
|
||||||
module TCP_client = struct
|
module TCP_client = struct
|
||||||
(** connect asynchronously *)
|
(** connect asynchronously *)
|
||||||
let rec connect_ sock addr =
|
let rec connect_ (sock : Fd.t) addr =
|
||||||
match Unix.connect sock addr with
|
match Unix.connect sock.fd addr with
|
||||||
| () -> ()
|
| () -> ()
|
||||||
| exception
|
| exception
|
||||||
Unix.Unix_error
|
Unix.Unix_error
|
||||||
|
|
@ -292,9 +292,9 @@ module TCP_client = struct
|
||||||
let sock = Unix.socket (Sockaddr.domain addr) Unix.SOCK_STREAM 0 in
|
let sock = Unix.socket (Sockaddr.domain addr) Unix.SOCK_STREAM 0 in
|
||||||
Unix.set_nonblock sock;
|
Unix.set_nonblock sock;
|
||||||
Unix.setsockopt sock Unix.TCP_NODELAY true;
|
Unix.setsockopt sock Unix.TCP_NODELAY true;
|
||||||
|
let sock = Fd.create sock in
|
||||||
|
|
||||||
connect_ sock addr;
|
connect_ sock addr;
|
||||||
let sock = Fd.create sock in
|
|
||||||
|
|
||||||
let finally () = Fd.close_noerr sock in
|
let finally () = Fd.close_noerr sock in
|
||||||
let@ () = Fun.protect ~finally in
|
let@ () = Fun.protect ~finally in
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@ module Action = struct
|
||||||
|
|
||||||
(** Action that we ask the lwt loop to perform, from the outside *)
|
(** Action that we ask the lwt loop to perform, from the outside *)
|
||||||
type t =
|
type t =
|
||||||
| Wait_readable of Unix.file_descr * cb * Cancel_handle.t
|
| Wait_readable of Fd.t * cb * Cancel_handle.t
|
||||||
| Wait_writable of Unix.file_descr * cb * Cancel_handle.t
|
| Wait_writable of Fd.t * cb * Cancel_handle.t
|
||||||
| Run_after_s of float * cb * Cancel_handle.t
|
| Run_after_s of float * cb * Cancel_handle.t
|
||||||
| Run_every_s of float * cb * Cancel_handle.t
|
| Run_every_s of float * cb * Cancel_handle.t
|
||||||
end
|
end
|
||||||
|
|
@ -55,12 +55,23 @@ end
|
||||||
|
|
||||||
module Per_fd = struct
|
module Per_fd = struct
|
||||||
type t = {
|
type t = {
|
||||||
fd: Unix.file_descr;
|
fd: Fd.t;
|
||||||
mutable reads: IO_wait.t list;
|
mutable reads: IO_wait.t list;
|
||||||
mutable writes: IO_wait.t list;
|
mutable writes: IO_wait.t list;
|
||||||
}
|
}
|
||||||
|
|
||||||
let[@inline] is_empty self = self.reads = [] && self.writes = []
|
let[@inline] is_empty self = self.reads = [] && self.writes = []
|
||||||
|
|
||||||
|
let update_event (self : t) ~(poll : Poll.t) : unit =
|
||||||
|
let ev =
|
||||||
|
match self.reads, self.writes with
|
||||||
|
| _ when Fd.closed self.fd -> Poll.Event.none
|
||||||
|
| [], [] -> Poll.Event.none
|
||||||
|
| _ :: _, [] -> Poll.Event.read
|
||||||
|
| [], _ :: _ -> Poll.Event.write
|
||||||
|
| _ :: _, _ :: _ -> Poll.Event.read_write
|
||||||
|
in
|
||||||
|
Poll.set poll self.fd.fd ev
|
||||||
end
|
end
|
||||||
|
|
||||||
(** Keep track of the subscriptions to channels *)
|
(** Keep track of the subscriptions to channels *)
|
||||||
|
|
@ -68,16 +79,18 @@ module IO_tbl = struct
|
||||||
type t = {
|
type t = {
|
||||||
mutable n_read: int;
|
mutable n_read: int;
|
||||||
mutable n_write: int;
|
mutable n_write: int;
|
||||||
|
poll: Poll.t;
|
||||||
tbl: (Unix.file_descr, Per_fd.t) Hashtbl.t;
|
tbl: (Unix.file_descr, Per_fd.t) Hashtbl.t;
|
||||||
}
|
}
|
||||||
|
|
||||||
let create () : t = { tbl = Hashtbl.create 32; n_read = 0; n_write = 0 }
|
let create ~poll () : t =
|
||||||
|
{ tbl = Hashtbl.create 32; n_read = 0; n_write = 0; poll }
|
||||||
|
|
||||||
let get_or_create (self : t) fd : Per_fd.t =
|
let get_or_create (self : t) (fd : Fd.t) : Per_fd.t =
|
||||||
try Hashtbl.find self.tbl fd
|
try Hashtbl.find self.tbl fd.fd
|
||||||
with Not_found ->
|
with Not_found ->
|
||||||
let per_fd = { Per_fd.fd; reads = []; writes = [] } in
|
let per_fd = { Per_fd.fd; reads = []; writes = [] } in
|
||||||
Hashtbl.add self.tbl fd per_fd;
|
Hashtbl.add self.tbl fd.fd per_fd;
|
||||||
per_fd
|
per_fd
|
||||||
|
|
||||||
let add_io_wait (self : t) fd mode (ev : IO_wait.t) =
|
let add_io_wait (self : t) fd mode (ev : IO_wait.t) =
|
||||||
|
|
@ -86,48 +99,51 @@ module IO_tbl = struct
|
||||||
match mode with
|
match mode with
|
||||||
| Read ->
|
| Read ->
|
||||||
self.n_read <- 1 + self.n_read;
|
self.n_read <- 1 + self.n_read;
|
||||||
per_fd.reads <- ev :: per_fd.reads
|
per_fd.reads <- ev :: per_fd.reads;
|
||||||
|
if self.n_read = 0 then Per_fd.update_event per_fd ~poll:self.poll
|
||||||
| Write ->
|
| Write ->
|
||||||
self.n_write <- 1 + self.n_write;
|
self.n_write <- 1 + self.n_write;
|
||||||
per_fd.writes <- ev :: per_fd.writes
|
per_fd.writes <- ev :: per_fd.writes;
|
||||||
|
if self.n_write = 0 then Per_fd.update_event per_fd ~poll:self.poll
|
||||||
|
|
||||||
let prepare_select (self : t) =
|
let[@inline] trigger_waiter (io : IO_wait.t) =
|
||||||
let reads = ref [] in
|
|
||||||
let writes = ref [] in
|
|
||||||
Hashtbl.iter
|
|
||||||
(fun _ (per_fd : Per_fd.t) ->
|
|
||||||
if Per_fd.is_empty per_fd then
|
|
||||||
Hashtbl.remove self.tbl per_fd.fd
|
|
||||||
else (
|
|
||||||
if per_fd.reads <> [] then reads := per_fd.fd :: !reads;
|
|
||||||
if per_fd.writes <> [] then writes := per_fd.fd :: !writes
|
|
||||||
))
|
|
||||||
self.tbl;
|
|
||||||
!reads, !writes
|
|
||||||
|
|
||||||
let trigger_waiter (io : IO_wait.t) =
|
|
||||||
if io.active then io.f io.as_cancel_handle
|
if io.active then io.f io.as_cancel_handle
|
||||||
|
|
||||||
let handle_ready ~ignore_read (self : t) (reads : Unix.file_descr list)
|
(** Wake up waiters on FDs who received events *)
|
||||||
(writes : Unix.file_descr list) : unit =
|
let handle_ready ~ignore_fd (self : t) : unit =
|
||||||
List.iter
|
let update_per_fd (per_fd : Per_fd.t) (event : Poll.Event.t) =
|
||||||
(fun fd ->
|
if Fd.closed per_fd.fd then
|
||||||
if fd <> ignore_read then (
|
(* cleanup *)
|
||||||
let per_fd = Hashtbl.find self.tbl fd in
|
Hashtbl.remove self.tbl per_fd.fd.fd
|
||||||
|
else (
|
||||||
|
if event.readable then (
|
||||||
List.iter trigger_waiter per_fd.reads;
|
List.iter trigger_waiter per_fd.reads;
|
||||||
self.n_read <- self.n_read - List.length per_fd.reads;
|
self.n_read <- self.n_read - List.length per_fd.reads;
|
||||||
per_fd.reads <- []
|
per_fd.reads <- []
|
||||||
))
|
);
|
||||||
reads;
|
|
||||||
|
|
||||||
List.iter
|
if event.writable then (
|
||||||
(fun fd ->
|
List.iter trigger_waiter per_fd.writes;
|
||||||
let per_fd = Hashtbl.find self.tbl fd in
|
self.n_write <- self.n_write - List.length per_fd.writes;
|
||||||
List.iter trigger_waiter per_fd.writes;
|
per_fd.writes <- []
|
||||||
self.n_write <- self.n_write - List.length per_fd.writes;
|
);
|
||||||
per_fd.writes <- [])
|
Per_fd.update_event per_fd ~poll:self.poll
|
||||||
writes;
|
)
|
||||||
|
in
|
||||||
|
|
||||||
|
Poll.iter_ready self.poll ~f:(fun fd event ->
|
||||||
|
if fd <> ignore_fd then (
|
||||||
|
let per_fd = Hashtbl.find self.tbl fd in
|
||||||
|
update_per_fd per_fd event
|
||||||
|
));
|
||||||
()
|
()
|
||||||
|
|
||||||
|
(** Remove closed FDs *)
|
||||||
|
let regular_cleanup (self : t) : unit =
|
||||||
|
Hashtbl.iter
|
||||||
|
(fun key (per_fd : Per_fd.t) ->
|
||||||
|
if Fd.closed per_fd.fd then Hashtbl.remove self.tbl key)
|
||||||
|
self.tbl
|
||||||
end
|
end
|
||||||
|
|
||||||
let run_timer_ (t : Timer.t) =
|
let run_timer_ (t : Timer.t) =
|
||||||
|
|
@ -154,19 +170,25 @@ module Ev_loop = struct
|
||||||
(** Is the ev loop thread currently waiting? *)
|
(** Is the ev loop thread currently waiting? *)
|
||||||
pipe_read: Unix.file_descr; (** Main thread only *)
|
pipe_read: Unix.file_descr; (** Main thread only *)
|
||||||
pipe_write: Unix.file_descr; (** Wakeup main thread *)
|
pipe_write: Unix.file_descr; (** Wakeup main thread *)
|
||||||
|
poll: Poll.t;
|
||||||
b4: bytes; (** small buffer *)
|
b4: bytes; (** small buffer *)
|
||||||
b1: bytes; (** small buffer *)
|
b1: bytes; (** small buffer *)
|
||||||
}
|
}
|
||||||
|
|
||||||
let create () : t =
|
let create () : t =
|
||||||
let pipe_read, pipe_write = Unix.pipe ~cloexec:true () in
|
let pipe_read, pipe_write = Unix.pipe ~cloexec:true () in
|
||||||
Unix.set_nonblock pipe_read;
|
|
||||||
(* pipe_write remains blocking *)
|
(* pipe_write remains blocking *)
|
||||||
|
Unix.set_nonblock pipe_read;
|
||||||
|
|
||||||
|
let poll = Poll.create () in
|
||||||
|
Poll.set poll pipe_read Poll.Event.read;
|
||||||
|
|
||||||
{
|
{
|
||||||
timer = Timer.create ();
|
timer = Timer.create ();
|
||||||
io_tbl = IO_tbl.create ();
|
io_tbl = IO_tbl.create ~poll ();
|
||||||
in_blocking_section = A.make false;
|
in_blocking_section = A.make false;
|
||||||
actions = Action_queue.create ();
|
actions = Action_queue.create ();
|
||||||
|
poll;
|
||||||
pipe_read;
|
pipe_read;
|
||||||
pipe_write;
|
pipe_write;
|
||||||
b4 = Bytes.create 4;
|
b4 = Bytes.create 4;
|
||||||
|
|
@ -207,21 +229,26 @@ module Ev_loop = struct
|
||||||
let run_step_ (self : t) : unit =
|
let run_step_ (self : t) : unit =
|
||||||
perform_pending_actions self;
|
perform_pending_actions self;
|
||||||
|
|
||||||
let delay = run_timer_ self.timer in
|
let delay_s = run_timer_ self.timer in
|
||||||
let delay = Option.value delay ~default:10. in
|
let delay_s = Option.value delay_s ~default:10. in
|
||||||
|
let timeout = Poll.Timeout.after Int64.(of_float (delay_s *. 1e9)) in
|
||||||
|
|
||||||
(* run [select] *)
|
(* run [select] *)
|
||||||
let reads, writes = IO_tbl.prepare_select self.io_tbl in
|
|
||||||
A.set self.in_blocking_section true;
|
A.set self.in_blocking_section true;
|
||||||
let reads, writes, _ =
|
let has_events =
|
||||||
let@ _sp = Tracing_.with_span "moonpool-unix.evloop.select" in
|
let@ _sp = Tracing_.with_span "moonpool-unix.evloop.select" in
|
||||||
Unix.select (self.pipe_read :: reads) writes [] delay
|
match Poll.wait self.poll timeout with
|
||||||
|
| `Timeout -> false
|
||||||
|
| `Ok -> true
|
||||||
in
|
in
|
||||||
|
|
||||||
A.set self.in_blocking_section false;
|
A.set self.in_blocking_section false;
|
||||||
|
|
||||||
drain_pipe_ self;
|
drain_pipe_ self;
|
||||||
IO_tbl.handle_ready ~ignore_read:self.pipe_read self.io_tbl reads writes;
|
if has_events then
|
||||||
|
IO_tbl.handle_ready ~ignore_fd:self.pipe_read self.io_tbl
|
||||||
|
else
|
||||||
|
IO_tbl.regular_cleanup self.io_tbl;
|
||||||
|
|
||||||
perform_pending_actions self;
|
perform_pending_actions self;
|
||||||
()
|
()
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,6 @@
|
||||||
(** Event loop *)
|
(** Event loop *)
|
||||||
|
|
||||||
val wait_readable :
|
val wait_readable : Fd.t -> Cancel_handle.t -> (Cancel_handle.t -> unit) -> unit
|
||||||
Unix.file_descr -> Cancel_handle.t -> (Cancel_handle.t -> unit) -> unit
|
val wait_writable : Fd.t -> Cancel_handle.t -> (Cancel_handle.t -> unit) -> unit
|
||||||
|
|
||||||
val wait_writable :
|
|
||||||
Unix.file_descr -> Cancel_handle.t -> (Cancel_handle.t -> unit) -> unit
|
|
||||||
|
|
||||||
val run_after_s : float -> Cancel_handle.t -> (Cancel_handle.t -> unit) -> unit
|
val run_after_s : float -> Cancel_handle.t -> (Cancel_handle.t -> unit) -> unit
|
||||||
val run_every_s : float -> Cancel_handle.t -> (Cancel_handle.t -> unit) -> unit
|
val run_every_s : float -> Cancel_handle.t -> (Cancel_handle.t -> unit) -> unit
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue