-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathatomic.mli
More file actions
158 lines (122 loc) · 6.73 KB
/
Copy pathatomic.mli
File metadata and controls
158 lines (122 loc) · 6.73 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
open! Base
(** An atomic (mutable) reference to a value of type ['a].
Atomic references mode cross both contention and portability, meaning they are always
uncontended and always portable, regardless of the kind of the ['a] type parameter, or
the mode of the atomic reference itself.
- They are always uncontended because mutating an atomic reference happens
{i atomically} - multiple fibers mutating the same atomic reference simultaneously
do not cause a data race.
- They are always portable because all the functions for creating or mutating an
atomic reference require the ['a] argument to be provided at the portable mode
For atomic integers, always mutate their value using one of the intrinsic operations
([fetch_and_add], [add], [sub], [logand], [logor], [logxor], [incr], or [decr]). For
atomic references to complex structures, use [update] to atomically update the atomic.
For example, to atomically add a value to a [Set]:
{[
open! Base
open! Portable
let atomically_add_to_set (set_atomic : Set.M(Int).t Atomic.t) value =
Atomic.update set_atomic ~pure_f:(fun set -> Set.add set value)
;;
]} *)
module Compare_failed_or_set_here : sig
(** The result of a call to [compare_and_set]. See the documentation of that function
for more information. *)
type t = Basement.Compare_failed_or_set_here.t =
| Compare_failed
| Set_here
[@@deriving sexp_of ~stackify]
end
type !'a t = 'a Basement.Portable_atomic.t
[%%rederive: type nonrec !'a t = 'a t [@@deriving sexp_of]]
[%%rederive: type nonrec !'a t = 'a t [@@deriving of_sexp]]
(** [make v] creates an atomic reference with initial value [v].
The optional [padded] argument specifies whether or not the atomic reference should be
padded to cache line size to avoid false sharing. When padded, i.e. [~padded:true], an
atomic reference occupies 4-16x the memory of one allocated without padding. By
default [padded] is [false].
When a CPU core attempts to perform a write, it takes exclusive ownership of the
entire cache line containing the memory location being written to. This means that
accessing disjoint memory locations sharing a cache line when at least one of those
accesses is a write is impossible. This is called false sharing. The cache coherence
traffic due to repeated invalidations can quickly become very expensive.
As a general guideline, it is typically beneficial to pad data structures that live
for a long time and are frequently accessed by multiple CPU cores and frequently
written to by at least one CPU core. *)
val make : 'a. ?padded:bool (** default:[false] *) -> 'a -> 'a t
(** [get r] gets the the current value of [r]. *)
external get : 'a. ('a t[@local_opt]) -> 'a = "%atomic_load"
(** [set r v] sets the value of [r] to [v] *)
external set : 'a. ('a t[@local_opt]) -> 'a -> unit = "caml_atomic_set_stub"
(** [exchange r v] sets the value of [r] to [v], and returns the previous value *)
external exchange : 'a. ('a t[@local_opt]) -> 'a -> 'a = "%atomic_exchange"
(** [compare_and_set r ~if_phys_equal_to ~replace_with] sets the new value of [r] to
[replace_with] {i only} if its current value is physically equal to [if_phys_equal_to]
-- the comparison and the set occur atomically. Returns [Set_here] if the value was
set to [replace_with] by this call to [compare_and_set], or [Compare_failed] if the
current value was not physically equal to [if_phys_equal_to] and hence the atomic
reference was left unchanged. *)
external compare_and_set
: 'a.
('a t[@local_opt])
-> if_phys_equal_to:'a
-> replace_with:'a
-> Compare_failed_or_set_here.t
= "%atomic_cas"
(** [compare_exchange r ~if_phys_equal_to ~replace_with] sets the new value of [r] to
[replace_with] only if its current value is physically equal to [if_phys_equal_to] --
the comparison and the set occur atomically. Returns the previous value of [r], or the
current (unchanged) value if the comparison failed. *)
external compare_exchange
: 'a.
('a t[@local_opt]) -> if_phys_equal_to:'a -> replace_with:'a -> 'a
= "caml_atomic_compare_exchange_stub"
(** [update t ~pure_f] atomically updates [t] to be the result of [pure_f (get t)].
[pure_f] may be called multiple times, so should be free of side effects. *)
val update : 'a. 'a t -> pure_f:('a -> 'a) -> unit
(** [get_and_update t ~pure_f] atomically updates [t] to be the result of
[pure_f (get t)]. [pure_f] may be called multiple times, so should be free of side
effects. Returns the old value. *)
val get_and_update : 'a. 'a t -> pure_f:('a -> 'a) -> 'a
(** [fetch_and_add r n] atomically increments the value of [r] by [n], and returns the
previous value (before the increment). *)
external fetch_and_add : (int t[@local_opt]) -> int -> int = "%atomic_fetch_add"
(** [add r i] atomically adds [i] to the value of [r]. *)
external add : (int t[@local_opt]) -> int -> unit = "caml_atomic_add_stub"
(** [sub r i] atomically subtracts [i] from the value of [r]. *)
external sub : (int t[@local_opt]) -> int -> unit = "caml_atomic_sub_stub"
(** [logand r i] atomically bitwise-ands [i] onto [r]. *)
external logand : (int t[@local_opt]) -> int -> unit = "caml_atomic_land_stub"
(** [logor r i] atomically bitwise-ands [i] onto [r]. *)
external logor : (int t[@local_opt]) -> int -> unit = "caml_atomic_lor_stub"
(** [logxor r i] atomically bitwise-xors [i] onto [r]. *)
external logxor : (int t[@local_opt]) -> int -> unit = "caml_atomic_lxor_stub"
(** [incr r] atomically increments the value of [r] by [1]. *)
val incr : int t -> unit
[@@zero_alloc]
(** [decr r] atomically decrements the value of [r] by [1]. *)
val decr : int t -> unit
[@@zero_alloc]
(** Operations on atomic lists *)
module List : sig
type nonrec 'a t = 'a list t
(** [push t a] atomically updates the atomic list [t] to have [a] as its first element. *)
val push : 'a. 'a t -> 'a -> unit
(** [pop t] atomically removes and returns the first element of the atomic list [t], or
returns [Null] if it is empty. *)
val pop : 'a t -> 'a Or_null.t
(** [pop_opt t] is like [pop], but it returns an [option] instead of [or_null], making
it usable with lists of [value_or_null] elements. *)
val pop_opt : 'a. 'a t -> 'a option
(** [pop_exn t] is like [pop], but it raises an exception if the list is empty. *)
val pop_exn : 'a. 'a t -> 'a
end
module Expert : sig
(** Load the value referenced by the given atomic, without using any compiler or
hardware fences.
This is dubiously safe, and has no explicit semantics within the OCaml memory
model - and may do the wrong thing entirely on backends with weak memory models such
as ARM. Use with caution! *)
val fenceless_get : 'a. 'a t -> 'a
[@@zero_alloc]
end