mirror of
https://github.com/c-cube/moonpool.git
synced 2025-12-12 14:00:41 -05:00
152 lines
5.8 KiB
OCaml
152 lines
5.8 KiB
OCaml
(** Futures.
|
|
|
|
A future of type ['a t] represents the result of a computation
|
|
that will yield a value of type ['a].
|
|
|
|
Typically, the computation is running on a thread pool {!Pool.t}
|
|
and will proceed on some worker. Once set, a future cannot change.
|
|
It either succeeds (storing a [Ok x] with [x: 'a]), or fail
|
|
(storing a [Error (exn, bt)] with an exception and the corresponding
|
|
backtrace).
|
|
|
|
Combinators such as {!map} and {!join_array} can be used to produce
|
|
futures from other futures (in a monadic way). Some combinators take
|
|
a [pool] argument to specify where the intermediate computation takes
|
|
place; for example [map ~pool ~f fut] maps the value in [fut]
|
|
using function [f], applicatively; the call to [f] happens on
|
|
pool [pool] (once [fut] resolves successfully with a value).
|
|
*)
|
|
|
|
type 'a or_error = ('a, exn * Printexc.raw_backtrace) result
|
|
|
|
type 'a t
|
|
(** A future with a result of type ['a]. *)
|
|
|
|
type 'a promise
|
|
(** A promise, which can be fulfilled exactly once to set
|
|
the corresponding future *)
|
|
|
|
val make : unit -> 'a t * 'a promise
|
|
(** Make a new future with the associated promise *)
|
|
|
|
val on_result : 'a t -> ('a or_error -> unit) -> unit
|
|
(** [on_result fut f] registers [f] to be called in the future
|
|
when [fut] is set ;
|
|
or calls [f] immediately if [fut] is already set. *)
|
|
|
|
exception Already_fulfilled
|
|
|
|
val fulfill : 'a promise -> 'a or_error -> unit
|
|
(** Fullfill the promise, setting the future at the same time.
|
|
@raise Already_fulfilled if the promise is already fulfilled. *)
|
|
|
|
val fulfill_idempotent : 'a promise -> 'a or_error -> unit
|
|
(** Fullfill the promise, setting the future at the same time.
|
|
Does nothing if the promise is already fulfilled. *)
|
|
|
|
val return : 'a -> 'a t
|
|
(** Already settled future, with a result *)
|
|
|
|
val fail : exn -> Printexc.raw_backtrace -> _ t
|
|
(** Already settled future, with a failure *)
|
|
|
|
val of_result : 'a or_error -> 'a t
|
|
|
|
val is_resolved : _ t -> bool
|
|
(** [is_resolved fut] is [true] iff [fut] is resolved. *)
|
|
|
|
val peek : 'a t -> 'a or_error option
|
|
(** [peek fut] returns [Some r] if [fut] is currently resolved with [r],
|
|
and [None] if [fut] is not resolved yet. *)
|
|
|
|
(** {2 Combinators} *)
|
|
|
|
val spawn : on:Pool.t -> (unit -> 'a) -> 'a t
|
|
(** [spaw ~on f] runs [f()] on the given pool, and return a future that will
|
|
hold its result. *)
|
|
|
|
val map : ?on:Pool.t -> f:('a -> 'b) -> 'a t -> 'b t
|
|
(** [map ?on ~f fut] returns a new future [fut2] that resolves
|
|
with [f x] if [fut] resolved with [x];
|
|
and fails with [e] if [fut] fails with [e] or [f x] raises [e].
|
|
@param on if provided, [f] runs on the given pool *)
|
|
|
|
val bind : ?on:Pool.t -> f:('a -> 'b t) -> 'a t -> 'b t
|
|
(** [map ?on ~f fut] returns a new future [fut2] that resolves
|
|
like the future [f x] if [fut] resolved with [x];
|
|
and fails with [e] if [fut] fails with [e] or [f x] raises [e].
|
|
@param on if provided, [f] runs on the given pool *)
|
|
|
|
val both : 'a t -> 'b t -> ('a * 'b) t
|
|
(** [both a b] succeeds with [x, y] if [a] succeeds with [x] and
|
|
[b] succeeds with [y], or fails if any of them fails. *)
|
|
|
|
val choose : 'a t -> 'b t -> ('a, 'b) Either.t t
|
|
(** [choose a b] succeeds [Left x] or [Right y] if [a] succeeds with [x] or
|
|
[b] succeeds with [y], or fails if both of them fails.
|
|
If they both succeed, it is not specified which result is used. *)
|
|
|
|
val choose_same : 'a t -> 'a t -> 'a t
|
|
(** [choose_same a b] succeeds with the value of one of [a] or [b] if
|
|
they succeed, or fails if both fail.
|
|
If they both succeed, it is not specified which result is used. *)
|
|
|
|
val join_array : 'a t array -> 'a array t
|
|
(** Wait for all the futures in the array. Fails if any future fails. *)
|
|
|
|
val join_list : 'a t list -> 'a list t
|
|
(** Wait for all the futures in the list. Fails if any future fails. *)
|
|
|
|
val wait_array : _ t array -> unit t
|
|
(** [wait_array arr] waits for all futures in [arr] to resolve. It discards
|
|
the individual results of futures in [arr]. It fails if any future fails. *)
|
|
|
|
val wait_list : _ t list -> unit t
|
|
(** [wait_list l] waits for all futures in [l] to resolve. It discards
|
|
the individual results of futures in [l]. It fails if any future fails. *)
|
|
|
|
val for_ : on:Pool.t -> int -> (int -> unit) -> unit t
|
|
(** [for_ ~on n f] runs [f 0], [f 1], …, [f (n-1)] on the pool, and returns
|
|
a future that resolves when all the tasks have resolved, or fails
|
|
as soon as one task has failed. *)
|
|
|
|
(** {2 Blocking} *)
|
|
|
|
val wait_block : 'a t -> 'a or_error
|
|
(** [wait_block fut] blocks the current thread until [fut] is resolved,
|
|
and returns its value.
|
|
|
|
{b NOTE}: A word of warning: this will monopolize the calling thread until the future
|
|
resolves. This can also easily cause deadlocks, if enough threads in a pool
|
|
call [wait_block] on futures running on the same pool or a pool depending on it.
|
|
|
|
A good rule to avoid deadlocks is to run this from outside of any pool,
|
|
or to have an acyclic order between pools where [wait_block]
|
|
is only called from a pool on futures evaluated in a pool that comes lower
|
|
in the hierarchy.
|
|
If this rule is broken, it is possible for all threads in a pool to wait
|
|
for futures that can only make progress on these same threads,
|
|
hence the deadlock.
|
|
*)
|
|
|
|
val wait_block_exn : 'a t -> 'a
|
|
(** Same as {!wait_block} but re-raises the exception if the future failed. *)
|
|
|
|
module type INFIX = sig
|
|
val ( >|= ) : 'a t -> ('a -> 'b) -> 'b t
|
|
val ( >>= ) : 'a t -> ('a -> 'b t) -> 'b t
|
|
val ( let+ ) : 'a t -> ('a -> 'b) -> 'b t
|
|
val ( and+ ) : 'a t -> 'b t -> ('a * 'b) t
|
|
val ( let* ) : 'a t -> ('a -> 'b t) -> 'b t
|
|
val ( and* ) : 'a t -> 'b t -> ('a * 'b) t
|
|
end
|
|
|
|
module Infix_local : INFIX
|
|
(** Operators that run on the same thread as the first future. *)
|
|
|
|
include INFIX
|
|
|
|
(** Make infix combinators *)
|
|
module Infix (_ : sig
|
|
val pool : Pool.t
|
|
end) : INFIX
|