Skip to content

Commit 162b0af

Browse files
committed
Add Mvar
1 parent edbfc68 commit 162b0af

File tree

8 files changed

+141
-2
lines changed

8 files changed

+141
-2
lines changed

src/kcas_data/kcas_data.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ module Stack = Stack
5656

5757
(** {1 Communication and synchronization primitives} *)
5858

59+
module Mvar = Mvar
5960
module Promise = Promise
6061

6162
(** {1 Linked data structures} *)

src/kcas_data/magic_option.ml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
open Kcas
2+
13
type 'a t = 'a
24

35
let none = ref ()
@@ -7,8 +9,11 @@ external some : 'a -> 'a t = "%identity"
79

810
let is_none x = x == none [@@inline]
911
let is_some x = x != none [@@inline]
10-
let get_or_retry x = if is_none x then Kcas.Retry.later () else x
12+
let get_or_retry x = if is_none x then Retry.later () else x [@@inline]
13+
let put_or_retry v x = if is_none x then some v else Retry.later () [@@inline]
14+
let take_or_retry x = if is_none x then Retry.later () else none [@@inline]
1115

1216
external get_unsafe : 'a t -> 'a = "%identity"
1317

14-
let to_option x = if is_none x then None else Some x
18+
let to_option x = if is_none x then None else Some x [@@inline]
19+
let of_option = function None -> none | Some x -> some x [@@inline]

src/kcas_data/magic_option.mli

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,8 @@ val some : 'a -> 'a t
77
val is_none : 'a t -> bool
88
val is_some : 'a t -> bool
99
val get_or_retry : 'a t -> 'a
10+
val put_or_retry : 'a -> 'a t -> 'a t
11+
val take_or_retry : 'a t -> 'a t
1012
val get_unsafe : 'a t -> 'a
1113
val to_option : 'a t -> 'a option
14+
val of_option : 'a option -> 'a t

src/kcas_data/mvar.ml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
open Kcas
2+
3+
type 'a t = 'a Magic_option.t Loc.t
4+
5+
let create x_opt = Loc.make (Magic_option.of_option x_opt)
6+
7+
module Xt = struct
8+
let is_empty ~xt mv = Magic_option.is_none (Xt.get ~xt mv)
9+
10+
let try_put ~xt mv value =
11+
Magic_option.is_none
12+
(Xt.compare_and_swap ~xt mv Magic_option.none (Magic_option.some value))
13+
14+
let put ~xt mv value =
15+
Xt.unsafe_modify ~xt mv (Magic_option.put_or_retry value)
16+
17+
let take_opt ~xt mv =
18+
Magic_option.to_option (Xt.exchange ~xt mv Magic_option.none)
19+
20+
let take ~xt mv =
21+
Magic_option.get_unsafe (Xt.unsafe_update ~xt mv Magic_option.take_or_retry)
22+
23+
let peek ~xt mv = Magic_option.get_or_retry (Xt.get ~xt mv)
24+
let peek_opt ~xt mv = Magic_option.to_option (Xt.get ~xt mv)
25+
end
26+
27+
let is_empty mv = Magic_option.is_none (Loc.get mv)
28+
let put mv value = Loc.modify mv (Magic_option.put_or_retry value)
29+
30+
let try_put mv value =
31+
Loc.compare_and_set mv Magic_option.none (Magic_option.some value)
32+
33+
let take mv = Magic_option.get_unsafe (Loc.update mv Magic_option.take_or_retry)
34+
let take_opt mv = Magic_option.to_option (Loc.exchange mv Magic_option.none)
35+
let peek mv = Loc.get_as Magic_option.get_or_retry mv
36+
let peek_opt mv = Magic_option.to_option (Loc.get mv)

src/kcas_data/mvar.mli

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
open Kcas
2+
3+
(** Synchronizing variable.
4+
5+
A synchronizing variable is essentially equivalent to a ['a option Loc.t]
6+
with blocking semantics on both {!take} and {!put}.
7+
8+
{b NOTE}: The current implementation is not guaranteed to be fair or
9+
scalable. In other words, when multiple producers block on {!put} or
10+
multiple consumers block on {!take} the operations are not queued and it is
11+
possible for a particular producer or consumer to starve. *)
12+
13+
(** {1 Common interface} *)
14+
15+
type !'a t
16+
(** The type of a synchronizing variable that may contain a value of type
17+
['a]. *)
18+
19+
val create : 'a option -> 'a t
20+
(** [create x_opt] returns a new synchronizing variable that will either be
21+
empty when [x_opt] is [None] or full when [x_opt] is [Some x]. *)
22+
23+
(** {1 Compositional interface} *)
24+
25+
module Xt :
26+
Mvar_intf.Ops
27+
with type 'a t := 'a t
28+
with type ('x, 'fn) fn := xt:'x Xt.t -> 'fn
29+
(** Explicit transaction passing on synchronizing variables. *)
30+
31+
(** {1 Non-compositional interface} *)
32+
33+
include Mvar_intf.Ops with type 'a t := 'a t with type ('x, 'fn) fn := 'fn

src/kcas_data/mvar_intf.ml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
module type Ops = sig
2+
type 'a t
3+
type ('x, 'fn) fn
4+
5+
val is_empty : ('x, 'a t -> bool) fn
6+
(** [is_empty mv] determines whether the synchronizing variable [mv] contains
7+
a value or not. *)
8+
9+
val put : ('x, 'a t -> 'a -> unit) fn
10+
(** [put mv x] fills the synchronizing variable [mv] with the value [v] or
11+
blocks until the variable becomes empty. *)
12+
13+
val try_put : ('x, 'a t -> 'a -> bool) fn
14+
(** [try_put mv x] tries to fill the synchronizing variable [mv] with the
15+
value [v] and returns [true] on success or [false] in case the variable is
16+
full. *)
17+
18+
val take : ('x, 'a t -> 'a) fn
19+
(** [take mv] removes and returns the current value of the synchronizing
20+
variable [mv] or blocks waiting until the variable is filled. *)
21+
22+
val take_opt : ('x, 'a t -> 'a option) fn
23+
(** [take_opt mv] removes and returns the current value of the synchronizing
24+
variable [mv] or returns [None] in case the variable is empty. *)
25+
26+
val peek : ('x, 'a t -> 'a) fn
27+
(** [peek mv] returns the current value of the synchronizing variable [mv] or
28+
blocks waiting until the variable is filled. *)
29+
30+
val peek_opt : ('x, 'a t -> 'a option) fn
31+
(** [peek_opt mv] returns the current value of the synchronizing variable [mv]
32+
or returns [None] in case the variable is empty. *)
33+
end

test/kcas_data/dune

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@
1616
(libraries kcas kcas_data)
1717
(package kcas_data))
1818

19+
(test
20+
(name mvar_test)
21+
(modules mvar_test)
22+
(libraries kcas kcas_data)
23+
(package kcas_data))
24+
1925
(test
2026
(name queue_test)
2127
(modules queue_test)

test/kcas_data/mvar_test.ml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
open Kcas
2+
open Kcas_data
3+
4+
let () =
5+
let mv = Mvar.create (Some 101) in
6+
assert (not (Mvar.is_empty mv));
7+
assert (Mvar.take mv = 101);
8+
assert (Mvar.is_empty mv);
9+
assert (Mvar.take_opt mv = None);
10+
Mvar.put mv 42;
11+
let running = Mvar.create None in
12+
let d =
13+
Domain.spawn @@ fun () ->
14+
Mvar.put running ();
15+
Xt.commit { tx = Mvar.Xt.put mv 76 }
16+
in
17+
assert (Mvar.take running = ());
18+
assert (Xt.commit { tx = Mvar.Xt.take mv } = 42);
19+
Domain.join d;
20+
assert (Mvar.take mv = 76);
21+
22+
Printf.printf "Test Mvar OK!\n%!"

0 commit comments

Comments
 (0)