Shell: improve Lwt_pipe
This commit is contained in:
parent
6b3e002285
commit
1f7f9b369d
@ -514,10 +514,10 @@ module Make (P: PARAMS) = struct
|
|||||||
let crypt buf =
|
let crypt buf =
|
||||||
let nonce = get_nonce remote_nonce in
|
let nonce = get_nonce remote_nonce in
|
||||||
Crypto_box.box my_secret_key public_key buf nonce in
|
Crypto_box.box my_secret_key public_key buf nonce in
|
||||||
let writer = Lwt_pipe.create 2 in
|
let writer = Lwt_pipe.create ~size:2 () in
|
||||||
let send p = Lwt_pipe.push writer p in
|
let send p = Lwt_pipe.push writer p in
|
||||||
let try_send p = Lwt_pipe.push_now writer p in
|
let try_send p = Lwt_pipe.push_now writer p in
|
||||||
let reader = Lwt_pipe.create 2 in
|
let reader = Lwt_pipe.create ~size:2 () in
|
||||||
let total_sent () = !sent in
|
let total_sent () = !sent in
|
||||||
let total_recv () = !received in
|
let total_recv () = !received in
|
||||||
let current_inflow () = received_ema#get in
|
let current_inflow () = received_ema#get in
|
||||||
@ -763,9 +763,9 @@ module Make (P: PARAMS) = struct
|
|||||||
(* a non exception-based cancelation mechanism *)
|
(* a non exception-based cancelation mechanism *)
|
||||||
let cancelation, cancel, on_cancel = Lwt_utils.canceler () in
|
let cancelation, cancel, on_cancel = Lwt_utils.canceler () in
|
||||||
(* create the internal event pipe *)
|
(* create the internal event pipe *)
|
||||||
let events = Lwt_pipe.create 100 in
|
let events = Lwt_pipe.create ~size:100 () in
|
||||||
(* create the external message pipe *)
|
(* create the external message pipe *)
|
||||||
let messages = Lwt_pipe.create 100 in
|
let messages = Lwt_pipe.create ~size:100 () in
|
||||||
(* fill the known peers pools from last time *)
|
(* fill the known peers pools from last time *)
|
||||||
Data_encoding_ezjsonm.read_file config.peers_file >>= fun res ->
|
Data_encoding_ezjsonm.read_file config.peers_file >>= fun res ->
|
||||||
let known_peers, black_list, my_gid,
|
let known_peers, black_list, my_gid,
|
||||||
|
@ -11,15 +11,25 @@ open Lwt.Infix
|
|||||||
|
|
||||||
type 'a t =
|
type 'a t =
|
||||||
{ queue : 'a Queue.t ;
|
{ queue : 'a Queue.t ;
|
||||||
size : int ;
|
size : int option ;
|
||||||
|
mutable closed : bool ;
|
||||||
mutable push_waiter : (unit Lwt.t * unit Lwt.u) option ;
|
mutable push_waiter : (unit Lwt.t * unit Lwt.u) option ;
|
||||||
mutable pop_waiter : (unit Lwt.t * unit Lwt.u) option }
|
mutable pop_waiter : (unit Lwt.t * unit Lwt.u) option ;
|
||||||
|
empty: unit Lwt_condition.t ;
|
||||||
|
full: unit Lwt_condition.t ;
|
||||||
|
not_full : unit Lwt_condition.t ;
|
||||||
|
}
|
||||||
|
|
||||||
let create ~size =
|
let create ?size () =
|
||||||
{ queue = Queue.create () ;
|
{ queue = Queue.create () ;
|
||||||
size ;
|
size ;
|
||||||
|
closed = false ;
|
||||||
push_waiter = None ;
|
push_waiter = None ;
|
||||||
pop_waiter = None }
|
pop_waiter = None ;
|
||||||
|
empty = Lwt_condition.create () ;
|
||||||
|
full = Lwt_condition.create () ;
|
||||||
|
not_full = Lwt_condition.create () ;
|
||||||
|
}
|
||||||
|
|
||||||
let notify_push q =
|
let notify_push q =
|
||||||
match q.push_waiter with
|
match q.push_waiter with
|
||||||
@ -37,69 +47,164 @@ let notify_pop q =
|
|||||||
|
|
||||||
let wait_push q =
|
let wait_push q =
|
||||||
match q.push_waiter with
|
match q.push_waiter with
|
||||||
| Some (t, _) -> t
|
| Some (t, _) -> Lwt.protected t
|
||||||
| None ->
|
| None ->
|
||||||
let waiter, wakener = Lwt.wait () in
|
let waiter, wakener = Lwt.wait () in
|
||||||
q.push_waiter <- Some (waiter, wakener) ;
|
q.push_waiter <- Some (waiter, wakener) ;
|
||||||
waiter
|
Lwt.protected waiter
|
||||||
|
|
||||||
let wait_pop q =
|
let wait_pop q =
|
||||||
match q.pop_waiter with
|
match q.pop_waiter with
|
||||||
| Some (t, _) -> t
|
| Some (t, _) -> Lwt.protected t
|
||||||
| None ->
|
| None ->
|
||||||
let waiter, wakener = Lwt.wait () in
|
let waiter, wakener = Lwt.wait () in
|
||||||
q.pop_waiter <- Some (waiter, wakener) ;
|
q.pop_waiter <- Some (waiter, wakener) ;
|
||||||
waiter
|
Lwt.protected waiter
|
||||||
|
|
||||||
let rec push ({ queue ; size } as q) elt =
|
let available_space { size } len =
|
||||||
if Queue.length queue < size then begin
|
match size with
|
||||||
|
| None -> true
|
||||||
|
| Some size -> len < size
|
||||||
|
|
||||||
|
let length { queue } = Queue.length queue
|
||||||
|
let is_empty { queue } = Queue.is_empty queue
|
||||||
|
let is_full ({ queue } as q) = not (available_space q (Queue.length queue))
|
||||||
|
|
||||||
|
let rec empty q =
|
||||||
|
if is_empty q
|
||||||
|
then Lwt.return_unit
|
||||||
|
else (Lwt_condition.wait q.empty >>= fun () -> empty q)
|
||||||
|
let rec full q =
|
||||||
|
if is_full q
|
||||||
|
then Lwt.return_unit
|
||||||
|
else (Lwt_condition.wait q.full >>= fun () -> full q)
|
||||||
|
let rec not_full q =
|
||||||
|
if not (is_empty q)
|
||||||
|
then Lwt.return_unit
|
||||||
|
else (Lwt_condition.wait q.not_full >>= fun () -> not_full q)
|
||||||
|
|
||||||
|
exception Closed
|
||||||
|
|
||||||
|
let rec push ({ closed ; queue ; full } as q) elt =
|
||||||
|
let len = Queue.length queue in
|
||||||
|
if closed then Lwt.fail Closed
|
||||||
|
else if available_space q len then begin
|
||||||
Queue.push elt queue ;
|
Queue.push elt queue ;
|
||||||
notify_push q ;
|
notify_push q ;
|
||||||
|
(if not (available_space q (len + 1)) then Lwt_condition.signal full ());
|
||||||
Lwt.return_unit
|
Lwt.return_unit
|
||||||
end else
|
end else
|
||||||
wait_pop q >>= fun () ->
|
wait_pop q >>= fun () ->
|
||||||
push q elt
|
push q elt
|
||||||
|
|
||||||
let rec push_now ({ queue; size } as q) elt =
|
let rec push_now ({ closed ; queue ; full } as q) elt =
|
||||||
Queue.length queue < size && begin
|
if closed then raise Closed ;
|
||||||
|
let len = Queue.length queue in
|
||||||
|
available_space q len && begin
|
||||||
Queue.push elt queue ;
|
Queue.push elt queue ;
|
||||||
notify_push q ;
|
notify_push q ;
|
||||||
|
(if not (available_space q (len + 1)) then Lwt_condition.signal full ()) ;
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
let rec pop ({ queue } as q) =
|
exception Full
|
||||||
|
|
||||||
|
let push_now_exn q elt =
|
||||||
|
if not (push_now q elt) then raise Full
|
||||||
|
|
||||||
|
let rec pop_all ({ closed ; queue ; empty ; not_full } as q) =
|
||||||
|
let was_full = is_full q in
|
||||||
|
if not (Queue.is_empty queue) then
|
||||||
|
let queue_copy = Queue.copy queue in
|
||||||
|
Queue.clear queue;
|
||||||
|
notify_pop q ;
|
||||||
|
(if was_full then Lwt_condition.signal not_full ());
|
||||||
|
Lwt_condition.signal empty ();
|
||||||
|
Lwt.return queue_copy
|
||||||
|
else if closed then
|
||||||
|
Lwt.fail Closed
|
||||||
|
else
|
||||||
|
wait_push q >>= fun () ->
|
||||||
|
pop_all q
|
||||||
|
|
||||||
|
let rec pop ({ closed ; queue ; empty ; not_full } as q) =
|
||||||
|
let was_full = is_full q in
|
||||||
if not (Queue.is_empty queue) then
|
if not (Queue.is_empty queue) then
|
||||||
let elt = Queue.pop queue in
|
let elt = Queue.pop queue in
|
||||||
notify_pop q ;
|
notify_pop q ;
|
||||||
|
(if was_full then Lwt_condition.signal not_full ());
|
||||||
|
(if Queue.length queue = 0 then Lwt_condition.signal empty ());
|
||||||
Lwt.return elt
|
Lwt.return elt
|
||||||
|
else if closed then
|
||||||
|
Lwt.fail Closed
|
||||||
else
|
else
|
||||||
wait_push q >>= fun () ->
|
wait_push q >>= fun () ->
|
||||||
pop q
|
pop q
|
||||||
|
|
||||||
let rec peek ({ queue } as q) =
|
let rec peek ({ closed ; queue } as q) =
|
||||||
if not (Queue.is_empty queue) then
|
if not (Queue.is_empty queue) then
|
||||||
let elt = Queue.peek queue in
|
let elt = Queue.peek queue in
|
||||||
Lwt.return elt
|
Lwt.return elt
|
||||||
|
else if closed then
|
||||||
|
Lwt.fail Closed
|
||||||
else
|
else
|
||||||
wait_push q >>= fun () ->
|
wait_push q >>= fun () ->
|
||||||
peek q
|
peek q
|
||||||
|
|
||||||
let pop_now_exn ({ queue } as q) =
|
exception Empty
|
||||||
|
|
||||||
|
let pop_now_exn ({ closed ; queue ; empty ; not_full } as q) =
|
||||||
|
let was_full = is_full q in
|
||||||
|
if Queue.is_empty queue then
|
||||||
|
(if closed then raise Closed else raise Empty) ;
|
||||||
let elt = Queue.pop queue in
|
let elt = Queue.pop queue in
|
||||||
|
(if was_full then Lwt_condition.signal not_full ());
|
||||||
|
(if Queue.length queue = 0 then Lwt_condition.signal empty ());
|
||||||
notify_pop q ;
|
notify_pop q ;
|
||||||
elt
|
elt
|
||||||
|
|
||||||
|
let pop_all_now ({ closed ; queue ; empty ; not_full } as q) =
|
||||||
|
let was_empty = is_empty q in
|
||||||
|
let was_full = is_full q in
|
||||||
|
if Queue.is_empty queue then
|
||||||
|
(if closed then raise Closed else raise Empty) ;
|
||||||
|
let queue_copy = Queue.copy queue in
|
||||||
|
Queue.clear queue ;
|
||||||
|
(if was_full then Lwt_condition.signal not_full ());
|
||||||
|
(if not was_empty then Lwt_condition.signal empty ());
|
||||||
|
notify_pop q ;
|
||||||
|
queue_copy
|
||||||
|
|
||||||
let pop_now q =
|
let pop_now q =
|
||||||
match pop_now_exn q with
|
match pop_now_exn q with
|
||||||
| exception Queue.Empty -> None
|
| exception Empty -> None
|
||||||
| elt -> Some elt
|
| elt -> Some elt
|
||||||
|
|
||||||
let length { queue } = Queue.length queue
|
|
||||||
let is_empty { queue } = Queue.is_empty queue
|
|
||||||
|
|
||||||
let rec values_available q =
|
let rec values_available q =
|
||||||
if is_empty q then
|
if is_empty q then
|
||||||
wait_push q >>= fun () ->
|
if q.closed then
|
||||||
values_available q
|
raise Closed
|
||||||
|
else
|
||||||
|
wait_push q >>= fun () ->
|
||||||
|
values_available q
|
||||||
else
|
else
|
||||||
Lwt.return_unit
|
Lwt.return_unit
|
||||||
|
|
||||||
|
let close q =
|
||||||
|
if not q.closed then begin
|
||||||
|
q.closed <- true ;
|
||||||
|
notify_push q ;
|
||||||
|
notify_pop q ;
|
||||||
|
Lwt_condition.broadcast_exn q.full Closed ;
|
||||||
|
end
|
||||||
|
|
||||||
|
let rec iter q ~f =
|
||||||
|
Lwt.catch begin fun () ->
|
||||||
|
pop q >>= fun elt ->
|
||||||
|
f elt >>= fun () ->
|
||||||
|
iter q ~f
|
||||||
|
end begin function
|
||||||
|
| Closed -> Lwt.return_unit
|
||||||
|
| exn -> Lwt.fail exn
|
||||||
|
end
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
type 'a t
|
type 'a t
|
||||||
(** Type of queues holding values of type ['a]. *)
|
(** Type of queues holding values of type ['a]. *)
|
||||||
|
|
||||||
val create : size:int -> 'a t
|
val create : ?size:int -> unit -> 'a t
|
||||||
(** [create ~size] is an empty queue that can hold max [size]
|
(** [create ~size] is an empty queue that can hold max [size]
|
||||||
elements. *)
|
elements. *)
|
||||||
|
|
||||||
@ -22,6 +22,10 @@ val push : 'a t -> 'a -> unit Lwt.t
|
|||||||
(** [push q v] is a thread that blocks while [q] contains more
|
(** [push q v] is a thread that blocks while [q] contains more
|
||||||
than [size] elements, then adds [v] at the end of [q]. *)
|
than [size] elements, then adds [v] at the end of [q]. *)
|
||||||
|
|
||||||
|
val pop_all : 'a t -> 'a Queue.t Lwt.t
|
||||||
|
(** [pop' q] is a thread that returns all elements in [q] or waits
|
||||||
|
till there is at least one element in [q]. *)
|
||||||
|
|
||||||
val pop : 'a t -> 'a Lwt.t
|
val pop : 'a t -> 'a Lwt.t
|
||||||
(** [pop q] is a thread that blocks while [q] is empty, then
|
(** [pop q] is a thread that blocks while [q] is empty, then
|
||||||
removes and returns the first element in [q]. *)
|
removes and returns the first element in [q]. *)
|
||||||
@ -38,10 +42,22 @@ val push_now : 'a t -> 'a -> bool
|
|||||||
(** [push_now q v] adds [v] at the ends of [q] immediately and returns
|
(** [push_now q v] adds [v] at the ends of [q] immediately and returns
|
||||||
[false] if [q] is currently full, [true] otherwise. *)
|
[false] if [q] is currently full, [true] otherwise. *)
|
||||||
|
|
||||||
|
exception Full
|
||||||
|
|
||||||
|
val push_now_exn : 'a t -> 'a -> unit
|
||||||
|
(** [push_now q v] adds [v] at the ends of [q] immediately or
|
||||||
|
raise [Full] if [q] is currently full. *)
|
||||||
|
|
||||||
|
val pop_all_now : 'a t -> 'a Queue.t
|
||||||
|
(** [pop_all_now q] is a copy of [q]'s internal queue, that may be
|
||||||
|
empty. *)
|
||||||
|
|
||||||
val pop_now : 'a t -> 'a option
|
val pop_now : 'a t -> 'a option
|
||||||
(** [pop_now q] maybe removes and returns the first element in [q] if
|
(** [pop_now q] maybe removes and returns the first element in [q] if
|
||||||
[q] contains at least one element. *)
|
[q] contains at least one element. *)
|
||||||
|
|
||||||
|
exception Empty
|
||||||
|
|
||||||
val pop_now_exn : 'a t -> 'a
|
val pop_now_exn : 'a t -> 'a
|
||||||
(** [pop_now_exn q] removes and returns the first element in [q] if
|
(** [pop_now_exn q] removes and returns the first element in [q] if
|
||||||
[q] contains at least one element, or raise [Empty] otherwise. *)
|
[q] contains at least one element, or raise [Empty] otherwise. *)
|
||||||
@ -52,3 +68,30 @@ val length : 'a t -> int
|
|||||||
val is_empty : 'a t -> bool
|
val is_empty : 'a t -> bool
|
||||||
(** [is_empty q] is [true] if [q] is empty, [false] otherwise. *)
|
(** [is_empty q] is [true] if [q] is empty, [false] otherwise. *)
|
||||||
|
|
||||||
|
val is_full : 'a t -> bool
|
||||||
|
(** [is_full q] is [true] if [q] is full, [false] otherwise. *)
|
||||||
|
|
||||||
|
val empty : 'a t -> unit Lwt.t
|
||||||
|
(** [empty q] returns when [q] becomes empty. *)
|
||||||
|
|
||||||
|
val full : 'a t -> unit Lwt.t
|
||||||
|
(** [full q] returns when [q] becomes full. *)
|
||||||
|
|
||||||
|
val not_full : 'a t -> unit Lwt.t
|
||||||
|
(** [not_full q] returns when [q] stop being full. *)
|
||||||
|
|
||||||
|
val iter : 'a t -> f:('a -> unit Lwt.t) -> unit Lwt.t
|
||||||
|
(** [iter q ~f] pops all elements of [q] and applies [f] on them. *)
|
||||||
|
|
||||||
|
exception Closed
|
||||||
|
|
||||||
|
val close : 'a t -> unit
|
||||||
|
(** [close q] the write end of [q]:
|
||||||
|
|
||||||
|
* Future write attempts will fail with [Closed].
|
||||||
|
* If there are reads blocked, they will unblock and fail with [Closed].
|
||||||
|
* Future read attempts will drain the data until there is no data left.
|
||||||
|
|
||||||
|
Thus, after a pipe has been closed, reads never block.
|
||||||
|
Close is idempotent.
|
||||||
|
*)
|
||||||
|
50
test/test_lwt_pipe.ml
Normal file
50
test/test_lwt_pipe.ml
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
open Lwt.Infix
|
||||||
|
include Logging.Make (struct let name = "test-pipe" end)
|
||||||
|
|
||||||
|
let rec producer queue = function
|
||||||
|
| 0 ->
|
||||||
|
lwt_log_notice "Done producing."
|
||||||
|
| n ->
|
||||||
|
Lwt_pipe.push queue () >>= fun () ->
|
||||||
|
producer queue (pred n)
|
||||||
|
|
||||||
|
let rec consumer queue = function
|
||||||
|
| 0 ->
|
||||||
|
lwt_log_notice "Done consuming."
|
||||||
|
| n ->
|
||||||
|
Lwt_pipe.pop queue >>= fun _ ->
|
||||||
|
consumer queue (pred n)
|
||||||
|
|
||||||
|
let rec gen acc f = function
|
||||||
|
| 0 -> acc
|
||||||
|
| n -> gen (f () :: acc) f (pred n)
|
||||||
|
|
||||||
|
let run qsize nbp nbc p c =
|
||||||
|
let q = Lwt_pipe.create qsize in
|
||||||
|
let producers = gen [] (fun () -> producer q p) nbp in
|
||||||
|
let consumers = gen [] (fun () -> consumer q c) nbc in
|
||||||
|
Lwt.join producers <&> Lwt.join consumers
|
||||||
|
|
||||||
|
let main () =
|
||||||
|
let qsize = ref 10 in
|
||||||
|
let nb_producers = ref 10 in
|
||||||
|
let nb_consumers = ref 10 in
|
||||||
|
let produced_per_producer = ref 10 in
|
||||||
|
let consumed_per_consumer = ref 10 in
|
||||||
|
let spec = Arg.[
|
||||||
|
"-qsize", Set_int qsize, "<int> Size of the pipe";
|
||||||
|
"-nc", Set_int nb_consumers, "<int> Number of consumers";
|
||||||
|
"-np", Set_int nb_producers, "<int> Number of producers";
|
||||||
|
"-n", Set_int consumed_per_consumer, "<int> Number of consumed items per consumers";
|
||||||
|
"-p", Set_int produced_per_producer, "<int> Number of produced items per producers";
|
||||||
|
"-v", Unit (fun () -> Lwt_log_core.(add_rule "*" Info)), " Log up to info msgs";
|
||||||
|
"-vv", Unit (fun () -> Lwt_log_core.(add_rule "*" Debug)), " Log up to debug msgs";
|
||||||
|
]
|
||||||
|
in
|
||||||
|
let anon_fun _ = () in
|
||||||
|
let usage_msg = "Usage: %s <num_peers>.\nArguments are:" in
|
||||||
|
Arg.parse spec anon_fun usage_msg;
|
||||||
|
run !qsize !nb_producers
|
||||||
|
!nb_consumers !produced_per_producer !consumed_per_consumer
|
||||||
|
|
||||||
|
let () = Lwt_main.run @@ main ()
|
Loading…
Reference in New Issue
Block a user