.TH "Lazy.Mutexed" 3 2026-06-22 OCamldoc "OCaml library" .SH NAME Lazy.Mutexed \- no description .SH Module Module Lazy.Mutexed .SH Documentation .sp Module .BI "Mutexed" : .B sig end .sp .sp .sp .sp .PP Simple mutex\-protected lazy thunks, which can be accessed from several domains or several threads\&. .sp This implementation has two downsides: .sp \-It is less optimized than .ft B Lazy\&.t .ft R \&. .sp \-It uses a standard library .ft B Mutex .ft R to wait on concurrent initialization, so it does not interoperate well with user\-level fiber/task abstractions (see the .ft B Lazy\&.Mutexed\&.force .ft R 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 .ft B \&'a Lazy\&.t .ft R is incorrect if the library may be used in concurrent settings\&. .ft B \&'a Lazy\&.Mutexed\&.t .ft R can be used, as long as the blocking behavior is acceptable\&. .sp Note: .ft B \&'a Lazy\&.t .ft R contains a protection against recursively forcing a thunk, it will raise .ft B Lazy\&.Undefined .ft R \&. On the other hand, .ft B \&'a Lazy\&.Mutexed\&.t .ft R will try to lock the mutex recursively, which will raise .ft B Sys_error .ft R \&. .sp See .ft B Lazy\&.Mutexed\&.examples .ft R below\&. .PP .I type .B !'a .I t .sp .sp .I val is_val : .B 'a t -> bool .sp .ft B is_val x .ft R returns .ft B true .ft R if the deferred computation .ft B x .ft R has already been forced and its result is a value, not an exception\&. .sp .I val from_val : .B 'a -> 'a t .sp .ft B from_val v .ft R is a deferred computation which is already finished and whose result is the value .ft B v .ft R \&. .sp .I val from_fun : .B (unit -> 'a) -> 'a t .sp .ft B from_fun f .ft R is a deferred computation that will take a mutex and call .ft B f .ft R when forced\&. .sp .I val force : .B 'a t -> 'a .sp .ft B force x .ft R forces the suspension .ft B x .ft R \&. If .ft B x .ft R has already been forced, .ft B Lazy\&.force x .ft R returns the same value again without recomputing it\&. If it raised an exception, the same exception is raised again\&. .sp If a concurrent call to .ft B force .ft R happens while the result is being computed, the caller will block on a .ft B Mutex .ft R \&. .sp .B "Raises Sys_error" if the suspension is forced on a thread where it is already being forced\&. This can happen if the thunk tries to force itself recursively, but it can also happen if the thunk code contains a `yield()` operation for a user\-level fiber/task abstraction, and two fibers/tasks try to force the thunk in parallel on the same domain: .EX .ft B .br \& (* This is wrong, a mutex\-protected thunk should not yield control\&. *) .br \& let thunk = Lazy\&.Mutexed\&.from_fun (fun () \-> \&.\&.\&. Fiber\&.yield () \&.\&.\&.) .br \& .br \& (* It may result in a same\-thread [Sys_error] exception below\&. *) .br \& let wrong = .br \& Fiber\&.both .br \& (fun () \-> Lazy\&.Mutexed\&.force thunk) .br \& (fun () \-> Lazy\&.Mutexed\&.force thunk) .br \& .ft R .EE .sp 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\&. .sp .PP .SS Examples .sp A typical use\-case is to initialize some library\-local state that is used by library functions\&. .sp .EX .ft B .br \& let config = Lazy\&.Mutexed\&.from_fun (fun () \-> .br \& match Sys\&.getenv_opt "MYLIB_CONFIG_PATH" with .br \& | None | Some "" \-> Config\&.default () .br \& | Some path \-> Config\&.read_from_path path .br \& ) .br \& .ft R .EE .sp .EX .ft B .br \& let entropy = .br \& (* we use a mibibyte of random data from /dev/urandom *) .br \& Lazy\&.Mutexed\&.from_fun (fun () \-> .br \& In_channel\&.with_open_bin "/dev/urandom" (fun chan \-> .br \& In_channel\&.really_input_string chan (1024 * 1024) .br \& ) .br \& ) .br \& .ft R .EE .PP