Module Domain_local_timeout

A scheduler independent timeout mechanism.

This is designed as a low level mechanism intended for writing higher level libraries that need to be able to have scheduler friendly timeouts.

A library that needs timeouts may simply call set_timeoutf.

To provide an efficient scheduler specific 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 then choose to use schedulers that provide the necessary implementation or, for example, use the default implementation by calling set_system.

The end result is effective interoperability between schedulers and concurrent programming libraries.

Interface for timeouts

val set_timeoutf : float -> (unit -> unit) -> unit -> unit

let cancel = set_timeoutf seconds action registers the action to be called after the specified time period in seconds has passed. The return value cancel is an idempotent and parallelism-safe action that can, and almost always should, be arranged to be called to cancel the timeout in case the timeout is no longer needed.

NOTE: The action must not raise exceptions or perform effects, should not block, and should usually just perform some minimal side-effect to e.g. unblock a fiber to do the work. With the default implementation, in case an action raises an exception, the timeout mechanism is disabled and subsequent set_timeoutf calls will raise the exception and the domain will also raise the exception at exit.

WARNING: It is allowed for the given action to be called e.g. from another thread outside of the scheduler from which set_timeoutf was called.

NOTE: Out of bounds values for seconds may be rejected by raising an Invalid_argument exception.

NOTE: Implementations should schedule timeouts using a monotonic clock.

Interface for schedulers

val using : set_timeoutf:(float -> (unit -> unit) -> unit -> unit) -> while_running:(unit -> 'a) -> 'a

using ~set_timeout ~while_running registers the given timeout mechanism for the current domain for the duration of running the given scheduler.

Default timeout implementation

module type Thread = sig ... end

Signature for a minimal subset of the Stdlib.Thread module needed by domain local timeout.

module type Unix = sig ... end

Signature for a minimal subset of the Stdlib.Unix module needed by domain local timeout.

val set_system : (module Thread) -> (module Unix) -> unit

set_system (module Thread) (module Unix) sets the system libraries to use for a default set_timeoutf implementation. This operation has an effect only on first time and, if needed, should be called from application code on the main domain before code using timeouts is called.

NOTE: The default set_timeoutf may not always be needed. It is possible for schedulers to use using to provide an implementation tailored for the scheduler.

Per thread configuration

val per_thread : (module Thread) -> unit

per_thread (module Thread) configures the current domain to store and select the timeout mechanism per systhread. This can be called at most once per domain before any calls to set_timeoutf.

The reason why this is an opt-in feature is that this allows domain local timeout to be implemented without depending on Thread which also depends on Unix.

Usage:

Domain.spawn @@ fun () ->
  Domain_local_timeout.per_thread (module Thread);

  (* ... *)

  ()
  |> Thread.create (fun () ->
     Domain_local_timeout.using
       ~set_timeoutf:set_timeoutf_for_scheduler_a
       ~while_running:scheduler_a);

  ()
  |> Thread.create (fun () ->
     Domain_local_timeout.using
       ~set_timeoutf:set_timeoutf_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.