Skip to content

Commit 79dc037

Browse files
committed
Add Atomic_array
1 parent 56f2f88 commit 79dc037

11 files changed

+227
-1
lines changed

src/Multicore_magic.mli

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,66 @@ module Transparent_atomic : sig
169169
val decr : int t -> unit
170170
end
171171

172+
(** {1 Missing functionality} *)
173+
174+
module Atomic_array : sig
175+
(** Array of (potentially unboxed) atomic locations.
176+
177+
Where available, this uses an undocumented operation exported by the OCaml
178+
5 runtime,
179+
{{:https://github.com/ocaml/ocaml/blob/7a5d882d22cdd32b6319e9be680bd1a3d67377a9/runtime/memory.c#L313-L338}
180+
[caml_atomic_cas_field]}, which makes it possible to perform sequentially
181+
consistent atomic updates of record fields and array elements.
182+
183+
Hopefully a future version of OCaml provides more comprehensive and even
184+
more efficient support for both sequentially consistent and relaxed atomic
185+
operations on records and arrays. *)
186+
187+
type !'a t
188+
(** Represents an array of atomic locations. *)
189+
190+
val make : int -> 'a -> 'a t
191+
(** [make n value] creates a new array of [n] atomic locations having given
192+
[value]. *)
193+
194+
val of_array : 'a array -> 'a t
195+
(** [of_array non_atomic_array] create a new array of atomic locations as a
196+
copy of the given [non_atomic_array]. *)
197+
198+
val init : int -> (int -> 'a) -> 'a t
199+
(** [init n fn] is equivalent to {{!of_array} [of_array (Array.init n fn)]}. *)
200+
201+
val length : 'a t -> int
202+
(** [length atomic_array] returns the length of the [atomic_array]. *)
203+
204+
val unsafe_fenceless_get : 'a t -> int -> 'a
205+
(** [unsafe_fenceless_get atomic_array index] reads and returns the value at
206+
the specified [index] of the [atomic_array].
207+
208+
⚠️ The read is {i relaxed} and may be reordered with respect to other reads
209+
and writes in program order.
210+
211+
⚠️ No bounds checking is performed. *)
212+
213+
val unsafe_fenceless_set : 'a t -> int -> 'a -> unit
214+
(** [unsafe_fenceless_set atomic_array index value] writes the given [value]
215+
to the specified [index] of the [atomic_array].
216+
217+
⚠️ The write is {i relaxed} and may be reordered with respect to other
218+
reads and (non-initializing) writes in program order.
219+
220+
⚠️ No bounds checking is performed. *)
221+
222+
val unsafe_compare_and_set : 'a t -> int -> 'a -> 'a -> bool
223+
(** [unsafe_compare_and_set atomic_array index before after] atomically
224+
updates the specified [index] of the [atomic_array] to the [after] value
225+
in case it had the [before] value and returns a boolean indicating whether
226+
that was the case. This operation is {i sequentially consistent} and may
227+
not be reordered with respect to other reads and writes in program order.
228+
229+
⚠️ No bounds checking is performed. *)
230+
end
231+
172232
(** {1 Avoiding contention} *)
173233

174234
val instantaneous_domain_index : unit -> int

src/boxed5/dune

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
(library
2+
(name multicore_magic_atomic_array_boxed5)
3+
(package multicore-magic)
4+
(enabled_if
5+
(and
6+
(<= 5.3.0 %{ocaml_version})))
7+
(wrapped false))
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
type 'a t = 'a Atomic.t array
2+
3+
let[@inline] at (type a) (xs : a t) i : a Atomic.t =
4+
(* ['a t] does not contain [float]s. *)
5+
Obj.magic (Array.unsafe_get (Obj.magic xs : a ref array) i)
6+
7+
let[@inline] make n v = Array.init n @@ fun _ -> Atomic.make v
8+
let[@inline] init n fn = Array.init n @@ fun i -> Atomic.make (fn i)
9+
let[@inline] of_array xs = init (Array.length xs) (Array.unsafe_get xs)
10+
11+
external length : 'a array -> int = "%array_length"
12+
13+
let[@inline] unsafe_fenceless_set xs i v = Obj.magic (at xs i) := v
14+
let[@inline] unsafe_fenceless_get xs i = !(Obj.magic (at xs i))
15+
16+
let[@inline] unsafe_compare_and_set xs i b a =
17+
Atomic.compare_and_set (at xs i) b a

src/dune

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
(library
22
(name Multicore_magic)
3-
(public_name multicore-magic))
3+
(public_name multicore-magic)
4+
(libraries
5+
(select
6+
multicore_magic.ml
7+
from
8+
(multicore_magic_atomic_array_unboxed5 -> multicore_magic.common.ml)
9+
(multicore_magic_atomic_array_boxed5 -> multicore_magic.common.ml)
10+
(multicore_magic_atomic_array_ocaml4 -> multicore_magic.common.ml))))
411

512
;;
613

src/Multicore_magic.ml renamed to src/multicore_magic.common.ml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,5 @@ let[@inline] fenceless_set (atomic : 'a Atomic.t) value =
99
(Obj.magic atomic : 'a ref) := value
1010

1111
let[@inline] fence atomic = Atomic.fetch_and_add atomic 0 |> ignore
12+
13+
module Atomic_array = Multicore_magic_atomic_array

src/ocaml4/dune

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
(library
2+
(name multicore_magic_atomic_array_ocaml4)
3+
(package multicore-magic)
4+
(enabled_if
5+
(< %{ocaml_version} 5.0.0))
6+
(wrapped false))
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
type !'a t = 'a array
2+
3+
let[@inline] unsafe_fenceless_set xs i x =
4+
(* We never create [float array]s. *)
5+
Array.unsafe_set (Obj.magic xs : string array) i (Obj.magic x)
6+
7+
let[@inline never] make n x =
8+
(* We never create [float array]s. *)
9+
if Obj.tag (Obj.repr x) != Obj.double_tag then Array.make n x
10+
else
11+
let xs = Array.make n (Obj.magic ()) in
12+
for i = 0 to n - 1 do
13+
unsafe_fenceless_set xs i x
14+
done;
15+
xs
16+
17+
let[@inline never] init n fn =
18+
(* We never create [float array]s. *)
19+
let ys = Array.make n (Obj.magic ()) in
20+
for i = 0 to n - 1 do
21+
unsafe_fenceless_set ys i (fn i)
22+
done;
23+
ys
24+
25+
let[@inline never] of_array xs =
26+
if Obj.tag (Obj.repr xs) != Obj.double_array_tag then Array.copy xs
27+
else init (Array.length xs) (fun i -> Array.unsafe_get xs i)
28+
29+
external length : 'a array -> int = "%array_length"
30+
31+
let[@inline] unsafe_fenceless_get xs i =
32+
(* We never create [float array]s. *)
33+
Obj.magic
34+
(Sys.opaque_identity (Array.unsafe_get (Obj.magic xs : string array) i))
35+
36+
let[@poll error] [@inline never] unsafe_compare_and_set (xs : string array) i b
37+
a =
38+
let before = Array.unsafe_get xs i in
39+
before == b
40+
&& begin
41+
Array.unsafe_set xs i a;
42+
true
43+
end
44+
45+
let[@inline] unsafe_compare_and_set (type a) (xs : a array) i (b : a) (a : a) =
46+
unsafe_compare_and_set
47+
(Obj.magic xs : string array)
48+
i (Obj.magic b) (Obj.magic a)

src/unboxed5/dune

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
(library
2+
(name multicore_magic_atomic_array_unboxed5)
3+
(package multicore-magic)
4+
(enabled_if
5+
(and
6+
(<= 5.0.0 %{ocaml_version})
7+
(< %{ocaml_version} 5.3.0)))
8+
(foreign_stubs
9+
(language c)
10+
(names multicore_magic_atomic_array))
11+
(wrapped false))
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#include "caml/mlvalues.h"
2+
#include "caml/memory.h"
3+
#include "caml/alloc.h"
4+
5+
CAMLprim value caml_multicore_magic_atomic_array_cas(
6+
value obj, intnat field, value oldval, value newval)
7+
{
8+
return Val_int(caml_atomic_cas_field(obj, Int_val(field), oldval, newval));
9+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
type !'a t = 'a array
2+
3+
let[@inline] unsafe_fenceless_set xs i x =
4+
(* We never create [float array]s. *)
5+
Array.unsafe_set (Obj.magic xs : string array) i (Obj.magic x)
6+
7+
let[@inline never] make n x =
8+
(* We never create [float array]s. *)
9+
if Obj.tag (Obj.repr x) != Obj.double_tag then Array.make n x
10+
else
11+
let xs = Array.make n (Obj.magic ()) in
12+
for i = 0 to n - 1 do
13+
unsafe_fenceless_set xs i x
14+
done;
15+
xs
16+
17+
let[@inline never] init n fn =
18+
(* We never create [float array]s. *)
19+
let ys = Array.make n (Obj.magic ()) in
20+
for i = 0 to n - 1 do
21+
unsafe_fenceless_set ys i (fn i)
22+
done;
23+
ys
24+
25+
let[@inline never] of_array xs =
26+
if Obj.tag (Obj.repr xs) != Obj.double_array_tag then Array.copy xs
27+
else init (Array.length xs) (fun i -> Array.unsafe_get xs i)
28+
29+
external length : 'a array -> int = "%array_length"
30+
31+
let[@inline] unsafe_fenceless_get xs i =
32+
(* We never create [float array]s. *)
33+
Obj.magic
34+
(Sys.opaque_identity (Array.unsafe_get (Obj.magic xs : string array) i))
35+
36+
let[@inline] unsafe_compare_and_set xs i b a : bool =
37+
let open struct
38+
external unsafe_compare_and_set_as_int :
39+
'a array -> (int[@untagged]) -> 'a -> 'a -> (int[@untagged])
40+
= "caml_multicore_magic_atomic_array_cas" "caml_atomic_cas_field"
41+
[@@noalloc]
42+
end in
43+
Obj.magic (unsafe_compare_and_set_as_int xs i b a)

0 commit comments

Comments
 (0)