package octez-libs
Hashtables with the signature S_ES
are Hashtbl-like with the following differences:
First, the module exports only a few functions in an attempt to limit the likelihood of race-conditions. Of particular interest is the following: in order to insert a value, one has to use `find_or_make` which either returns an existing promise for a value bound to the given key, or makes such a promise. It is not possible to insert another value for an existing key. This limits the table (e.g., it can only hold one value for any given key), but it forces the user to *atomically* test membership and insert an element.
Second, the table is automatically cleaned. Specifically, when a promise for a value is fulfilled with an Error _
, the binding is removed. This leads to the following behavior:
(* setup *)
let t = create 256 in
let () = assert (length t = 0) in
(* insert a first promise for a value *)
let p, r = Lwt.task () in
let i1 = find_or_make t 1 (fun () -> p) in
let () = assert (length t = 1) in
(* because the same key is used, the promise is not inserted. *)
let i2 = find_or_make t 1 (fun () -> assert false) in
let () = assert (length t = 1) in
(* when the original promise errors, the binding is removed *)
let () = Lwt.wakeup r (Error ..) in
let () = assert (length t = 0) in
(* and both [find_or_make] promises have the error *)
let () = match Lwt.state i1 with
| Return (Error ..) -> ()
| _ -> assert false
in
let () = match Lwt.state i2 with
| Return (Error ..) -> ()
| _ -> assert false
in
This automatic cleaning relieves the user from the responsibility of cleaning the table (which is another possible source of race condition).
For consistency, traversal functions ignore Error _
and rejections.
Third, every time a promise is removed from the table (be it by clean
, reset
, or just remove
), the promise is canceled.
val create : int -> ('a, 'trace) t
val clear : ('a, 'trace) t -> unit
clear tbl
cancels and removes all the promises in tbl
.
val reset : ('a, 'trace) t -> unit
reset tbl
cancels and removes all the promises in tbl
, and resizes tbl
to its initial size.
val find_or_make :
('a, 'trace) t ->
key ->
(unit -> ('a, 'trace) result Lwt.t) ->
('a, 'trace) result Lwt.t
find_or_make tbl k make
behaves differently depending on k
being bound in tbl
:
- if
k
is bound intbl
thenfind_or_make tbl k make
returns the promisep
thatk
is bound to. Thisp
might be already fulfilled withOk _
or it might be pending. Thisp
cannot be already fulfilled withError _
or already rejected. This is becauseError
/rejected promises are removed from the table automatically. Note however that if thisp
is pending,p
might become fulfilled withError _
or become rejected. - if
k
is not bound intbl
thenmake ()
is called and the returned promisep
is bound tok
intbl
. Thenp
is returned. Whenp
is resolved, it may be removed automatically fromtbl
as described above.
remove tbl k
cancels the promise bound to k
in tbl
and removes it. If k
is not bound in tbl
it does nothing.
val iter_with_waiting_es :
(key -> 'a -> (unit, 'trace) result Lwt.t) ->
('a, 'trace) t ->
(unit, 'trace) result Lwt.t
iter_with_waiting_es f tbl
iterates f
over the bindings in tbl
.
Specifically, for each binding (k, p)
it waits for p
to be fulfilled with Ok v
and calls f k v
. If p
fulfills with Error _
or is rejected, then no call to f
is made for this binding. Note however that an Error
/rejection in one promise returned by f
interrupts the iteration.
It processes bindings one after the other: it waits for both the bound promise to resolve and then the call promise to resolve before continuing to the next binding.
val iter_with_waiting_ep :
(key -> 'a -> (unit, 'error) result Lwt.t) ->
('a, 'error) t ->
(unit, 'error list) result Lwt.t
iter_with_waiting_ep f tbl
iterates f
over the bindings in tbl
.
Specifically, for each binding (k, p)
it waits for p
to be fulfilled with Ok v
and calls f k v
. If p
fulfills with Error _
or is rejected, then no call is made for this binding.
Note however that if one (or more) of the promises returned by f
ends in Error
/rejection, the final result of this promise is an Error
/rejection. Even so, it only resolves once all the promises have.
It processes all bindings concurrently: it concurrently waits for all the bound promises to resolve and calls f
as they resolve.
val fold_with_waiting_es :
(key -> 'a -> 'b -> ('b, 'trace) result Lwt.t) ->
('a, 'trace) t ->
'b ->
('b, 'trace) result Lwt.t
fold_with_waiting_es f tbl init
folds init
with f
over the bindings in tbl
.
Specifically, for each binding (k, p)
it waits for p
to be fulfilled with Ok v
and determines the next accumulator by calling f k v acc
. If p
fulfills with Error _
or is rejected, then no call is made for this binding.
It processes bindings one after the other.
fold_promises f tbl init
folds over the table, passing the raw promises to f
. This means that f
can observe Error
/rejections.
This can be used to, e.g., count the number of resolved/unresolved promises.
fold_resolved f tbl init
folds over the already resolved promises of tbl
. More specifically, it folds over the v
for all the promises fulfilled with Ok v
that are bound in tbl
.
val length : ('a, 'trace) t -> int
val stats : ('a, 'trace) t -> Hashtbl.statistics