Domain_local_await
A scheduler independent blocking mechanism.
This is designed as a low level mechanism intended for writing higher level libraries that need to block in a scheduler friendly manner.
A library that needs to suspend and later resume the current thread of execution may simply call prepare_for_await
to obtain a pair of await
and release
operations for the purpose.
To provide an efficient and scheduler friendly implementation of the mechanism, schedulers may install an implementation by wrapping the scheduler main loop with a call to using
. The implementation is then stored in a domain, and optionally thread, local variable. The overhead that this imposes on a scheduler should be insignificant.
An application can the choose to use schedulers that provide the necessary implementation. An implementation that works with plain domains and threads is provided as a default.
The end result is effective interoperability between schedulers and concurrent programming libraries.
type t = {
release : unit -> unit;
t.release ()
resumes the corresponding caller of t.await ()
or does nothing in case the corresponding t.await ()
has already resumed or the target fiber has been canceled.
NOTE: An implementation of t.release ()
should never fail.
await : unit -> unit;
t.await ()
suspends the caller at most until t.release ()
is called.
}
val prepare_for_await : unit -> t
prepare_for_await ()
prepares and returns a trigger t
for at most one use of t.await ()
by calling the prepare
function registered for the current domain.
prepare_for_await
and t.await
are allowed to raise an (unspecified) exception that indicates that the caller's fiber has been canceled (and should terminate). If an exception is raised, then the caller should perform whatever cleanup is necessary to e.g. avoid space leaks.
NOTE: It is allowed for two different calls of prepare_for_await
to return the same trigger and e.g. share a single trigger per domain or per fiber or even just have one single trigger.
val using : prepare_for_await:(unit -> t) -> while_running:(unit -> 'a) -> 'a
using ~prepare_for_await ~while_running
registers the given asynchronous trigger mechanism for the current domain, or, if the domain has been configured to use per_thread
schedulers, the current systhread, for the duration of running the given scheduler. In other words, this sets the implementation of prepare_for_await
for blocking under the scheduler.
NOTE: The given prepare_for_await
function is called every time prepare_for_await
is called while the scheduler is running.
NOTE: This is normally only called by libraries that implement schedulers and the specified prepare_for_await
typically returns a trigger mechanism t
that tightly integrates with the scheduler by e.g. performing an effect to suspend the current fiber when t.await
is called.
module type Thread = sig ... end
Signature for a minimal subset of the Stdlib.Thread
module needed by domain local await.
val per_thread : (module Thread) -> unit
per_thread (module Thread)
configures the current domain to store and select the trigger mechanism per systhread. This can be called at most once per domain before any calls to prepare_for_await
.
The reason why this is an opt-in feature is that this allows domain local await to be implemented without depending on Thread
which also depends on Unix
.
Usage:
Domain.spawn @@ fun () ->
Domain_local_await.per_thread (module Thread);
(* ... *)
()
|> Thread.create (fun () ->
Domain_local_await.using
~prepare_for_await:prepare_for_scheduler_a
~while_running:scheduler_a);
()
|> Thread.create (fun () ->
Domain_local_await.using
~prepare_for_await:prepare_for_scheduler_b
~while_running:scheduler_b);
(* ... *)
NOTE: It is not necessary to use per systhread configuration on a domain unless you want different systhreads to use different schedulers.