Line data Source code
1 : #ifndef AWS_COMMON_ATOMICS_H
2 : #define AWS_COMMON_ATOMICS_H
3 :
4 : #include <aws/common/common.h>
5 :
6 : /**
7 : * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
8 : * SPDX-License-Identifier: Apache-2.0.
9 : */
10 :
11 : /**
12 : * struct aws_atomic_var represents an atomic variable - a value which can hold an integer or pointer
13 : * that can be manipulated atomically. struct aws_atomic_vars should normally only be manipulated
14 : * with atomics methods defined in this header.
15 : */
16 : struct aws_atomic_var {
17 : void *value;
18 : };
19 : /* Helpers for extracting the integer and pointer values from aws_atomic_var. */
20 145838 : #define AWS_ATOMIC_VAR_PTRVAL(var) ((var)->value)
21 0 : #define AWS_ATOMIC_VAR_INTVAL(var) (*(aws_atomic_impl_int_t *)(var))
22 :
23 : /*
24 : * This enumeration specifies the memory ordering properties requested for a particular
25 : * atomic operation. The atomic operation may provide stricter ordering than requested.
26 : * Note that, within a single thread, all operations are still sequenced (that is, a thread
27 : * sees its own atomic writes and reads happening in program order, but other threads may
28 : * disagree on this ordering).
29 : *
30 : * The behavior of these memory orderings are the same as in the C11 atomics API; however,
31 : * we only implement a subset that can be portably implemented on the compilers we target.
32 : */
33 :
34 : enum aws_memory_order {
35 : /**
36 : * No particular ordering constraints are guaranteed relative to other
37 : * operations at all; we merely ensure that the operation itself is atomic.
38 : */
39 : aws_memory_order_relaxed = 0,
40 : /* aws_memory_order_consume - not currently implemented */
41 :
42 : /**
43 : * Specifies acquire ordering. No reads or writes on the current thread can be
44 : * reordered to happen before this operation. This is typically paired with a release
45 : * ordering; any writes that happened on the releasing operation will be visible
46 : * after the paired acquire operation.
47 : *
48 : * Acquire ordering is only meaningful on load or load-store operations.
49 : */
50 : aws_memory_order_acquire = 2, /* leave a spot for consume if we ever add it */
51 :
52 : /**
53 : * Specifies release order. No reads or writes can be reordered to come after this
54 : * operation. Typically paired with an acquire operation.
55 : *
56 : * Release ordering is only meaningful on store or load-store operations.
57 : */
58 : aws_memory_order_release,
59 :
60 : /**
61 : * Specifies acquire-release order; if this operation acts as a load, it acts as an
62 : * acquire operation; if it acts as a store, it acts as a release operation; if it's
63 : * a load-store, it does both.
64 : */
65 : aws_memory_order_acq_rel,
66 :
67 : /*
68 : * Specifies sequentially consistent order. This behaves as acq_rel, but in addition,
69 : * all seq_cst operations appear to occur in some globally consistent order.
70 : *
71 : * TODO: Figure out how to correctly implement this in MSVC. It appears that interlocked
72 : * functions provide only acq_rel ordering.
73 : */
74 : aws_memory_order_seq_cst
75 : };
76 :
77 : /**
78 : * Statically initializes an aws_atomic_var to a given size_t value.
79 : */
80 : #define AWS_ATOMIC_INIT_INT(x) \
81 : { .value = (void *)(uintptr_t)(x) }
82 :
83 : /**
84 : * Statically initializes an aws_atomic_var to a given void * value.
85 : */
86 : #define AWS_ATOMIC_INIT_PTR(x) \
87 : { .value = (void *)(x) }
88 :
89 : AWS_EXTERN_C_BEGIN
90 :
91 : /*
92 : * Note: We do not use the C11 atomics API; this is because we want to make sure the representation
93 : * (and behavior) of atomic values is consistent, regardless of what --std= flag you pass to your compiler.
94 : * Since C11 atomics can silently introduce locks, we run the risk of creating such ABI inconsistencies
95 : * if we decide based on compiler features which atomics API to use, and in practice we expect to have
96 : * either the GNU or MSVC atomics anyway.
97 : *
98 : * As future work, we could test to see if the C11 atomics API on this platform behaves consistently
99 : * with the other APIs and use it if it does.
100 : */
101 :
102 : /**
103 : * Initializes an atomic variable with an integer value. This operation should be done before any
104 : * other operations on this atomic variable, and must be done before attempting any parallel operations.
105 : *
106 : * This operation does not imply a barrier. Ensure that you use an acquire-release barrier (or stronger)
107 : * when communicating the fact that initialization is complete to the other thread. Launching the thread
108 : * implies a sufficiently strong barrier.
109 : */
110 : AWS_STATIC_IMPL
111 : void aws_atomic_init_int(volatile struct aws_atomic_var *var, size_t n);
112 :
113 : /**
114 : * Initializes an atomic variable with a pointer value. This operation should be done before any
115 : * other operations on this atomic variable, and must be done before attempting any parallel operations.
116 : *
117 : * This operation does not imply a barrier. Ensure that you use an acquire-release barrier (or stronger)
118 : * when communicating the fact that initialization is complete to the other thread. Launching the thread
119 : * implies a sufficiently strong barrier.
120 : */
121 : AWS_STATIC_IMPL
122 : void aws_atomic_init_ptr(volatile struct aws_atomic_var *var, void *p);
123 :
124 : /**
125 : * Reads an atomic var as an integer, using the specified ordering, and returns the result.
126 : */
127 : AWS_STATIC_IMPL
128 : size_t aws_atomic_load_int_explicit(volatile const struct aws_atomic_var *var, enum aws_memory_order memory_order);
129 :
130 : /**
131 : * Reads an atomic var as an integer, using sequentially consistent ordering, and returns the result.
132 : */
133 : AWS_STATIC_IMPL
134 : size_t aws_atomic_load_int(volatile const struct aws_atomic_var *var);
135 : /**
136 : * Reads an atomic var as a pointer, using the specified ordering, and returns the result.
137 : */
138 : AWS_STATIC_IMPL
139 : void *aws_atomic_load_ptr_explicit(volatile const struct aws_atomic_var *var, enum aws_memory_order memory_order);
140 :
141 : /**
142 : * Reads an atomic var as a pointer, using sequentially consistent ordering, and returns the result.
143 : */
144 : AWS_STATIC_IMPL
145 : void *aws_atomic_load_ptr(volatile const struct aws_atomic_var *var);
146 :
147 : /**
148 : * Stores an integer into an atomic var, using the specified ordering.
149 : */
150 : AWS_STATIC_IMPL
151 : void aws_atomic_store_int_explicit(volatile struct aws_atomic_var *var, size_t n, enum aws_memory_order memory_order);
152 :
153 : /**
154 : * Stores an integer into an atomic var, using sequentially consistent ordering.
155 : */
156 : AWS_STATIC_IMPL
157 : void aws_atomic_store_int(volatile struct aws_atomic_var *var, size_t n);
158 :
159 : /**
160 : * Stores a pointer into an atomic var, using the specified ordering.
161 : */
162 : AWS_STATIC_IMPL
163 : void aws_atomic_store_ptr_explicit(volatile struct aws_atomic_var *var, void *p, enum aws_memory_order memory_order);
164 :
165 : /**
166 : * Stores a pointer into an atomic var, using sequentially consistent ordering.
167 : */
168 : AWS_STATIC_IMPL
169 : void aws_atomic_store_ptr(volatile struct aws_atomic_var *var, void *p);
170 :
171 : /**
172 : * Exchanges an integer with the value in an atomic_var, using the specified ordering.
173 : * Returns the value that was previously in the atomic_var.
174 : */
175 : AWS_STATIC_IMPL
176 : size_t aws_atomic_exchange_int_explicit(
177 : volatile struct aws_atomic_var *var,
178 : size_t n,
179 : enum aws_memory_order memory_order);
180 :
181 : /**
182 : * Exchanges an integer with the value in an atomic_var, using sequentially consistent ordering.
183 : * Returns the value that was previously in the atomic_var.
184 : */
185 : AWS_STATIC_IMPL
186 : size_t aws_atomic_exchange_int(volatile struct aws_atomic_var *var, size_t n);
187 :
188 : /**
189 : * Exchanges a pointer with the value in an atomic_var, using the specified ordering.
190 : * Returns the value that was previously in the atomic_var.
191 : */
192 : AWS_STATIC_IMPL
193 : void *aws_atomic_exchange_ptr_explicit(
194 : volatile struct aws_atomic_var *var,
195 : void *p,
196 : enum aws_memory_order memory_order);
197 :
198 : /**
199 : * Exchanges an integer with the value in an atomic_var, using sequentially consistent ordering.
200 : * Returns the value that was previously in the atomic_var.
201 : */
202 : AWS_STATIC_IMPL
203 : void *aws_atomic_exchange_ptr(volatile struct aws_atomic_var *var, void *p);
204 :
205 : /**
206 : * Atomically compares *var to *expected; if they are equal, atomically sets *var = desired. Otherwise, *expected is set
207 : * to the value in *var. On success, the memory ordering used was order_success; otherwise, it was order_failure.
208 : * order_failure must be no stronger than order_success, and must not be release or acq_rel.
209 : * Returns true if the compare was successful and the variable updated to desired.
210 : */
211 : AWS_STATIC_IMPL
212 : bool aws_atomic_compare_exchange_int_explicit(
213 : volatile struct aws_atomic_var *var,
214 : size_t *expected,
215 : size_t desired,
216 : enum aws_memory_order order_success,
217 : enum aws_memory_order order_failure);
218 :
219 : /**
220 : * Atomically compares *var to *expected; if they are equal, atomically sets *var = desired. Otherwise, *expected is set
221 : * to the value in *var. Uses sequentially consistent memory ordering, regardless of success or failure.
222 : * Returns true if the compare was successful and the variable updated to desired.
223 : */
224 : AWS_STATIC_IMPL
225 : bool aws_atomic_compare_exchange_int(volatile struct aws_atomic_var *var, size_t *expected, size_t desired);
226 :
227 : /**
228 : * Atomically compares *var to *expected; if they are equal, atomically sets *var = desired. Otherwise, *expected is set
229 : * to the value in *var. On success, the memory ordering used was order_success; otherwise, it was order_failure.
230 : * order_failure must be no stronger than order_success, and must not be release or acq_rel.
231 : * Returns true if the compare was successful and the variable updated to desired.
232 : */
233 : AWS_STATIC_IMPL
234 : bool aws_atomic_compare_exchange_ptr_explicit(
235 : volatile struct aws_atomic_var *var,
236 : void **expected,
237 : void *desired,
238 : enum aws_memory_order order_success,
239 : enum aws_memory_order order_failure);
240 :
241 : /**
242 : * Atomically compares *var to *expected; if they are equal, atomically sets *var = desired. Otherwise, *expected is set
243 : * to the value in *var. Uses sequentially consistent memory ordering, regardless of success or failure.
244 : * Returns true if the compare was successful and the variable updated to desired.
245 : */
246 : AWS_STATIC_IMPL
247 : bool aws_atomic_compare_exchange_ptr(volatile struct aws_atomic_var *var, void **expected, void *desired);
248 :
249 : /**
250 : * Atomically adds n to *var, and returns the previous value of *var.
251 : */
252 : AWS_STATIC_IMPL
253 : size_t aws_atomic_fetch_add_explicit(volatile struct aws_atomic_var *var, size_t n, enum aws_memory_order order);
254 :
255 : /**
256 : * Atomically subtracts n from *var, and returns the previous value of *var.
257 : */
258 : AWS_STATIC_IMPL
259 : size_t aws_atomic_fetch_sub_explicit(volatile struct aws_atomic_var *var, size_t n, enum aws_memory_order order);
260 :
261 : /**
262 : * Atomically ORs n with *var, and returns the previous value of *var.
263 : */
264 : AWS_STATIC_IMPL
265 : size_t aws_atomic_fetch_or_explicit(volatile struct aws_atomic_var *var, size_t n, enum aws_memory_order order);
266 :
267 : /**
268 : * Atomically ANDs n with *var, and returns the previous value of *var.
269 : */
270 : AWS_STATIC_IMPL
271 : size_t aws_atomic_fetch_and_explicit(volatile struct aws_atomic_var *var, size_t n, enum aws_memory_order order);
272 :
273 : /**
274 : * Atomically XORs n with *var, and returns the previous value of *var.
275 : */
276 : AWS_STATIC_IMPL
277 : size_t aws_atomic_fetch_xor_explicit(volatile struct aws_atomic_var *var, size_t n, enum aws_memory_order order);
278 :
279 : /**
280 : * Atomically adds n to *var, and returns the previous value of *var.
281 : * Uses sequentially consistent ordering.
282 : */
283 : AWS_STATIC_IMPL
284 : size_t aws_atomic_fetch_add(volatile struct aws_atomic_var *var, size_t n);
285 :
286 : /**
287 : * Atomically subtracts n from *var, and returns the previous value of *var.
288 : * Uses sequentially consistent ordering.
289 : */
290 : AWS_STATIC_IMPL
291 : size_t aws_atomic_fetch_sub(volatile struct aws_atomic_var *var, size_t n);
292 :
293 : /**
294 : * Atomically ands n into *var, and returns the previous value of *var.
295 : * Uses sequentially consistent ordering.
296 : */
297 : AWS_STATIC_IMPL
298 : size_t aws_atomic_fetch_and(volatile struct aws_atomic_var *var, size_t n);
299 :
300 : /**
301 : * Atomically ors n into *var, and returns the previous value of *var.
302 : * Uses sequentially consistent ordering.
303 : */
304 : AWS_STATIC_IMPL
305 : size_t aws_atomic_fetch_or(volatile struct aws_atomic_var *var, size_t n);
306 :
307 : /**
308 : * Atomically xors n into *var, and returns the previous value of *var.
309 : * Uses sequentially consistent ordering.
310 : */
311 : AWS_STATIC_IMPL
312 : size_t aws_atomic_fetch_xor(volatile struct aws_atomic_var *var, size_t n);
313 :
314 : /**
315 : * Provides the same reordering guarantees as an atomic operation with the specified memory order, without
316 : * needing to actually perform an atomic operation.
317 : */
318 : AWS_STATIC_IMPL
319 : void aws_atomic_thread_fence(enum aws_memory_order order);
320 :
321 : #ifndef AWS_NO_STATIC_IMPL
322 : # include <aws/common/atomics.inl>
323 : #endif /* AWS_NO_STATIC_IMPL */
324 :
325 : AWS_EXTERN_C_END
326 :
327 : #endif
|