ligo/src/utils/lwt_pipe.ml

211 lines
5.6 KiB
OCaml
Raw Normal View History

2016-11-29 00:46:26 +04:00
(**************************************************************************)
(* *)
(* Copyright (c) 2014 - 2016. *)
(* Dynamic Ledger Solutions, Inc. <contact@tezos.com> *)
(* *)
(* All rights reserved. No warranty, explicit or implicit, provided. *)
(* *)
(**************************************************************************)
open Lwt.Infix
type 'a t =
{ queue : 'a Queue.t ;
2017-01-14 16:13:30 +04:00
size : int option ;
mutable closed : bool ;
2016-11-29 00:46:26 +04:00
mutable push_waiter : (unit Lwt.t * unit Lwt.u) option ;
2017-01-14 16:13:30 +04:00
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 ;
}
2016-11-29 00:46:26 +04:00
2017-01-14 16:13:30 +04:00
let create ?size () =
2016-11-29 00:46:26 +04:00
{ queue = Queue.create () ;
size ;
2017-01-14 16:13:30 +04:00
closed = false ;
2016-11-29 00:46:26 +04:00
push_waiter = None ;
2017-01-14 16:13:30 +04:00
pop_waiter = None ;
empty = Lwt_condition.create () ;
full = Lwt_condition.create () ;
not_full = Lwt_condition.create () ;
}
2016-11-29 00:46:26 +04:00
let notify_push q =
match q.push_waiter with
| None -> ()
| Some (_, w) ->
q.push_waiter <- None ;
Lwt.wakeup_later w ()
let notify_pop q =
match q.pop_waiter with
| None -> ()
| Some (_, w) ->
q.pop_waiter <- None ;
Lwt.wakeup_later w ()
let wait_push q =
match q.push_waiter with
2017-01-14 16:13:30 +04:00
| Some (t, _) -> Lwt.protected t
2016-11-29 00:46:26 +04:00
| None ->
let waiter, wakener = Lwt.wait () in
q.push_waiter <- Some (waiter, wakener) ;
2017-01-14 16:13:30 +04:00
Lwt.protected waiter
2016-11-29 00:46:26 +04:00
let wait_pop q =
match q.pop_waiter with
2017-01-14 16:13:30 +04:00
| Some (t, _) -> Lwt.protected t
2016-11-29 00:46:26 +04:00
| None ->
let waiter, wakener = Lwt.wait () in
q.pop_waiter <- Some (waiter, wakener) ;
2017-01-14 16:13:30 +04:00
Lwt.protected waiter
2016-11-29 00:46:26 +04:00
2017-01-14 16:13:30 +04:00
let available_space { size } len =
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
2016-11-29 00:46:26 +04:00
Queue.push elt queue ;
notify_push q ;
2017-01-14 16:13:30 +04:00
(if not (available_space q (len + 1)) then Lwt_condition.signal full ());
2016-11-29 00:46:26 +04:00
Lwt.return_unit
end else
wait_pop q >>= fun () ->
push q elt
2017-01-14 16:13:30 +04:00
let rec push_now ({ closed ; queue ; full } as q) elt =
if closed then raise Closed ;
let len = Queue.length queue in
available_space q len && begin
2016-11-29 00:46:26 +04:00
Queue.push elt queue ;
notify_push q ;
2017-01-14 16:13:30 +04:00
(if not (available_space q (len + 1)) then Lwt_condition.signal full ()) ;
2016-11-29 00:46:26 +04:00
true
end
2017-01-14 16:13:30 +04:00
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
2016-11-29 00:46:26 +04:00
if not (Queue.is_empty queue) then
let elt = Queue.pop queue in
notify_pop q ;
2017-01-14 16:13:30 +04:00
(if was_full then Lwt_condition.signal not_full ());
(if Queue.length queue = 0 then Lwt_condition.signal empty ());
2016-11-29 00:46:26 +04:00
Lwt.return elt
2017-01-14 16:13:30 +04:00
else if closed then
Lwt.fail Closed
2016-11-29 00:46:26 +04:00
else
wait_push q >>= fun () ->
pop q
2017-01-14 16:13:30 +04:00
let rec peek ({ closed ; queue } as q) =
2016-11-29 00:46:26 +04:00
if not (Queue.is_empty queue) then
let elt = Queue.peek queue in
Lwt.return elt
2017-01-14 16:13:30 +04:00
else if closed then
Lwt.fail Closed
2016-11-29 00:46:26 +04:00
else
wait_push q >>= fun () ->
peek q
2017-01-14 16:13:30 +04:00
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) ;
2016-11-29 00:46:26 +04:00
let elt = Queue.pop queue in
2017-01-14 16:13:30 +04:00
(if was_full then Lwt_condition.signal not_full ());
(if Queue.length queue = 0 then Lwt_condition.signal empty ());
2016-11-29 00:46:26 +04:00
notify_pop q ;
elt
2017-01-14 16:13:30 +04:00
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
2016-11-29 00:46:26 +04:00
let pop_now q =
match pop_now_exn q with
2017-01-14 16:13:30 +04:00
| exception Empty -> None
2016-11-29 00:46:26 +04:00
| elt -> Some elt
let rec values_available q =
if is_empty q then
2017-01-14 16:13:30 +04:00
if q.closed then
raise Closed
else
wait_push q >>= fun () ->
values_available q
2016-11-29 00:46:26 +04:00
else
Lwt.return_unit
2017-01-14 16:13:30 +04:00
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