Module Lazy.Mutexed

module Mutexed: sig .. end

Simple mutex-protected lazy thunks, which can be accessed from several domains or several threads.

This implementation has two downsides:

A typical use-case is optional library initialization code that is moderately expensive, or acquires resources. The library author does not want to do this work on startup, because it may not be needed, but using 'Lazy.t is incorrect if the library may be used in concurrent settings. 'Lazy.Mutexed.t can be used, as long as the blocking behavior is acceptable.

Note: 'Lazy.t contains a protection against recursively forcing a thunk, it will raise Lazy.Undefined. On the other hand, 'Lazy.Mutexed.t will try to lock the mutex recursively, which will raise Sys_error.

See the examples below.

type !'a t 
val is_val : 'a t -> bool

is_val x returns true if the deferred computation x has already been forced and its result is a value, not an exception.

val from_val : 'a -> 'a t

from_val v is a deferred computation which is already finished and whose result is the value v.

val from_fun : (unit -> 'a) -> 'a t

from_fun f is a deferred computation that will take a mutex and call f when forced.

val force : 'a t -> 'a

force x forces the suspension x. If x has already been forced, Lazy.force x returns the same value again without recomputing it. If it raised an exception, the same exception is raised again.

If a concurrent call to force happens while the result is being computed, the caller will block on a Mutex.

Examples

A typical use-case is to initialize some library-local state that is used by library functions.

        let config = Lazy.Mutexed.from_fun (fun () ->
          match Sys.getenv_opt "MYLIB_CONFIG_PATH" with
          | None | Some "" -> Config.default ()
          | Some path -> Config.read_from_path path
        )
      
        let entropy =
          (* we use a mibibyte of random data from /dev/urandom *)
          Lazy.Mutexed.from_fun (fun () ->
            In_channel.with_open_bin "/dev/urandom" (fun chan ->
              In_channel.really_input_string chan (1024 * 1024)
            )
          )