Lazy.MutexedSourceSimple mutex-protected lazy thunks, which can be accessed from several domains or several threads.
This implementation has two downsides:
Lazy.t.Mutex to wait on concurrent initialization, so it does not interoperate well with user-level fiber/task abstractions (see the Lazy.Mutexed.force documentation for a dangerous example).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 'a Lazy.t is incorrect if the library may be used in concurrent settings. 'a Lazy.Mutexed.t can be used, as long as the blocking behavior is acceptable.
Note: 'a Lazy.t contains a protection against recursively forcing a thunk, it will raise Undefined. On the other hand, 'a Lazy.Mutexed.t will try to lock the mutex recursively, which will raise Sys_error.
See the examples below.
is_val x returns true if the deferred computation x has already been forced and its result is a value, not an exception.
from_val v is a deferred computation which is already finished and whose result is the value v.
from_fun f is a deferred computation that will take a mutex and call f when forced.
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.
To avoid such errors, you should ensure that mutex-protected thunks never yield control outside of their sub-computation -- by not using a user-level thread library within it, or by using appropriate blocking/masking functions if available.
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)
)
)