feat: add a Lock module

This commit is contained in:
Simon Cruanes 2023-07-09 16:50:47 -04:00
parent b07d460b3f
commit 2852741360
No known key found for this signature in database
GPG key ID: EBFFF6F283F3A2B4
4 changed files with 73 additions and 0 deletions

36
src/lock.ml Normal file
View file

@ -0,0 +1,36 @@
type 'a t = {
mutex: Mutex.t;
mutable content: 'a;
}
let create content : _ t = { mutex = Mutex.create (); content }
let with_ (self : _ t) f =
Mutex.lock self.mutex;
try
let x = f self.content in
Mutex.unlock self.mutex;
x
with e ->
Mutex.unlock self.mutex;
raise e
let[@inline] mutex self = self.mutex
let[@inline] update self f = with_ self (fun x -> self.content <- f x)
let[@inline] update_map l f =
with_ l (fun x ->
let x', y = f x in
l.content <- x';
y)
let get l =
Mutex.lock l.mutex;
let x = l.content in
Mutex.unlock l.mutex;
x
let set l x =
Mutex.lock l.mutex;
l.content <- x;
Mutex.unlock l.mutex

35
src/lock.mli Normal file
View file

@ -0,0 +1,35 @@
(** Mutex-protected resource.
@since NEXT_RELEASE *)
type 'a t
(** A value protected by a mutex *)
val create : 'a -> 'a t
(** Create a new protected value. *)
val with_ : 'a t -> ('a -> 'b) -> 'b
(** [with_ l f] runs [f x] where [x] is the value protected with
the lock [l], in a critical section. If [f x] fails, [with_lock l f]
fails too but the lock is released. *)
val update : 'a t -> ('a -> 'a) -> unit
(** [update l f] replaces the content [x] of [l] with [f x], while protected
by the mutex. *)
val update_map : 'a t -> ('a -> 'a * 'b) -> 'b
(** [update_map l f] computes [x', y = f (get l)], then puts [x'] in [l]
and returns [y], while protected by the mutex. *)
val mutex : _ t -> Mutex.t
(** Underlying mutex. *)
val get : 'a t -> 'a
(** Atomically get the value in the lock. The value that is returned
isn't protected! *)
val set : 'a t -> 'a -> unit
(** Atomically set the value.
{b NOTE} caution: using {!get} and {!set} as if this were a {!ref}
is an anti pattern and will not protect data against some race conditions. *)

View file

@ -5,6 +5,7 @@ let start_thread_on_some_domain f x =
module Atomic = Atomic_ module Atomic = Atomic_
module Blocking_queue = Bb_queue module Blocking_queue = Bb_queue
module Chan = Chan module Chan = Chan
module Lock = Lock
module Fork_join = Fork_join module Fork_join = Fork_join
module Fut = Fut module Fut = Fut
module Pool = Pool module Pool = Pool

View file

@ -11,6 +11,7 @@ val start_thread_on_some_domain : ('a -> unit) -> 'a -> Thread.t
to run the thread. This ensures that we don't always pick the same domain to run the thread. This ensures that we don't always pick the same domain
to run all the various threads needed in an application (timers, event loops, etc.) *) to run all the various threads needed in an application (timers, event loops, etc.) *)
module Lock = Lock
module Fut = Fut module Fut = Fut
module Chan = Chan module Chan = Chan
module Fork_join = Fork_join module Fork_join = Fork_join