]> git.proxmox.com Git - mirror_ovs.git/blob - lib/ovs-atomic-msvc.h
Windows: Changing explicit typecasts to fix C++ compilation issues
[mirror_ovs.git] / lib / ovs-atomic-msvc.h
1 /*
2 * Copyright (c) 2014 Nicira, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 /* This header implements atomic operation primitives for MSVC
18 * on i586 or greater platforms (32 bit). */
19 #ifndef IN_OVS_ATOMIC_H
20 #error "This header should only be included indirectly via ovs-atomic.h."
21 #endif
22
23 /* From msdn documentation: With Visual Studio 2003, volatile to volatile
24 * references are ordered; the compiler will not re-order volatile variable
25 * access. With Visual Studio 2005, the compiler also uses acquire semantics
26 * for read operations on volatile variables and release semantics for write
27 * operations on volatile variables (when supported by the CPU).
28 *
29 * Though there is no clear documentation that states that anything greater
30 * than VS 2005 has the same behavior as described above, looking through MSVCs
31 * C++ atomics library in VS2013 shows that the compiler still takes
32 * acquire/release semantics on volatile variables. */
33 #define ATOMIC(TYPE) TYPE volatile
34
35 typedef enum {
36 memory_order_relaxed,
37 memory_order_consume,
38 memory_order_acquire,
39 memory_order_release,
40 memory_order_acq_rel,
41 memory_order_seq_cst
42 } memory_order;
43
44 #if _MSC_VER > 1800 && defined(_M_IX86)
45 /* From WDK 10 _InlineInterlocked* functions are renamed to
46 * _InlineInterlocked* although the documentation does not specify it */
47 #define _InterlockedExchangeAdd64 _InlineInterlockedExchangeAdd64
48 #define _InterlockedExchange64 _InlineInterlockedExchange64
49 #endif
50
51 #define ATOMIC_BOOL_LOCK_FREE 2
52 #define ATOMIC_CHAR_LOCK_FREE 2
53 #define ATOMIC_SHORT_LOCK_FREE 2
54 #define ATOMIC_INT_LOCK_FREE 2
55 #define ATOMIC_LONG_LOCK_FREE 2
56 #define ATOMIC_LLONG_LOCK_FREE 2
57 #define ATOMIC_POINTER_LOCK_FREE 2
58
59 #define IS_LOCKLESS_ATOMIC(OBJECT) \
60 (sizeof(OBJECT) <= 8 && IS_POW2(sizeof(OBJECT)))
61
62 #define ATOMIC_VAR_INIT(VALUE) (VALUE)
63 #define atomic_init(OBJECT, VALUE) (*(OBJECT) = (VALUE), (void) 0)
64
65 static inline void
66 atomic_compiler_barrier(memory_order order)
67 {
68 /* In case of 'memory_order_consume', it is implicitly assumed that
69 * the compiler will not move instructions that have data-dependency
70 * on the variable in question before the barrier. */
71 if (order > memory_order_consume) {
72 _ReadWriteBarrier();
73 }
74 }
75
76 static inline void
77 atomic_thread_fence(memory_order order)
78 {
79 /* x86 is strongly ordered and acquire/release semantics come
80 * automatically. */
81 atomic_compiler_barrier(order);
82 if (order == memory_order_seq_cst) {
83 MemoryBarrier();
84 atomic_compiler_barrier(order);
85 }
86 }
87
88 static inline void
89 atomic_signal_fence(memory_order order)
90 {
91 atomic_compiler_barrier(order);
92 }
93
94 /* 1, 2 and 4 bytes loads and stores are atomic on aligned memory. In addition,
95 * since the compiler automatically takes acquire and release semantics on
96 * volatile variables, for any order lesser than 'memory_order_seq_cst', we
97 * can directly assign or read values. */
98
99 #define atomic_store32(DST, SRC, ORDER) \
100 if (ORDER == memory_order_seq_cst) { \
101 InterlockedExchange((long volatile *) (DST), \
102 (long) (SRC)); \
103 } else { \
104 *(DST) = (SRC); \
105 }
106
107 /* MSVC converts 64 bit writes into two instructions. So there is
108 * a possibility that an interrupt can make a 64 bit write non-atomic even
109 * when 8 byte aligned. So use InterlockedExchange64().
110 *
111 * For atomic stores, 'consume' and 'acquire' semantics are not valid. But we
112 * are using 'Exchange' to get atomic stores here and we only have
113 * InterlockedExchange64(), InterlockedExchangeNoFence64() and
114 * InterlockedExchange64Acquire() available. So we are forced to use
115 * InterlockedExchange64() which uses full memory barrier for everything
116 * greater than 'memory_order_relaxed'. */
117 #ifdef _M_IX86
118 #define atomic_store64(DST, SRC, ORDER) \
119 if (ORDER == memory_order_relaxed) { \
120 InterlockedExchangeNoFence64((int64_t volatile *) (DST), \
121 (int64_t) (SRC)); \
122 } else { \
123 InterlockedExchange64((int64_t volatile *) (DST), (int64_t) (SRC));\
124 }
125 #elif _M_X64
126 /* 64 bit writes are atomic on amd64 if 64 bit aligned. */
127 #define atomic_store64(DST, SRC, ORDER) \
128 atomic_storeX(64, DST, SRC, ORDER)
129 #endif
130
131 #define atomic_store8(DST, SRC, ORDER) \
132 if (ORDER == memory_order_seq_cst) { \
133 InterlockedExchange8((char volatile *) (DST), (char) (SRC)); \
134 } else { \
135 *(DST) = (SRC); \
136 }
137
138 #define atomic_store16(DST, SRC, ORDER) \
139 if (ORDER == memory_order_seq_cst) { \
140 InterlockedExchange16((short volatile *) (DST), (short) (SRC)); \
141 } else { \
142 *(DST) = (SRC); \
143 }
144
145 #define atomic_store(DST, SRC) \
146 atomic_store_explicit(DST, SRC, memory_order_seq_cst)
147
148 #define atomic_store_explicit(DST, SRC, ORDER) \
149 if (sizeof *(DST) == 1) { \
150 atomic_store8(DST, SRC, ORDER) \
151 } else if (sizeof *(DST) == 2) { \
152 atomic_store16( DST, SRC, ORDER) \
153 } else if (sizeof *(DST) == 4) { \
154 atomic_store32(DST, SRC, ORDER) \
155 } else if (sizeof *(DST) == 8) { \
156 atomic_store64(DST, SRC, ORDER) \
157 } else { \
158 abort(); \
159 }
160
161 /* On x86, for 'memory_order_seq_cst', if stores are locked, the corresponding
162 * reads don't need to be locked (based on the following in Intel Developers
163 * manual:
164 * “Locked operations are atomic with respect to all other memory operations
165 * and all externally visible events. Only instruction fetch and page table
166 * accesses can pass locked instructions. Locked instructions can be used to
167 * synchronize data written by one processor and read by another processor.
168 * For the P6 family processors, locked operations serialize all outstanding
169 * load and store operations (that is, wait for them to complete). This rule
170 * is also true for the Pentium 4 and Intel Xeon processors, with one
171 * exception. Load operations that reference weakly ordered memory types
172 * (such as the WC memory type) may not be serialized."). */
173
174 /* For 8, 16 and 32 bit variations. */
175 #define atomic_readX(SRC, DST, ORDER) \
176 *(DST) = *(SRC);
177
178 /* MSVC converts 64 bit reads into two instructions. So there is
179 * a possibility that an interrupt can make a 64 bit read non-atomic even
180 * when 8 byte aligned. So use fully memory barrier InterlockedOr64(). */
181 #ifdef _M_IX86
182 #define atomic_read64(SRC, DST, ORDER) \
183 __pragma (warning(push)) \
184 __pragma (warning(disable:4047)) \
185 *(DST) = InterlockedOr64((int64_t volatile *) (SRC), 0); \
186 __pragma (warning(pop))
187 #elif _M_X64
188 /* 64 bit reads are atomic on amd64 if 64 bit aligned. */
189 #define atomic_read64(SRC, DST, ORDER) \
190 *(DST) = *(SRC);
191 #endif
192
193 #define atomic_read(SRC, DST) \
194 atomic_read_explicit(SRC, DST, memory_order_seq_cst)
195
196 #define atomic_read_explicit(SRC, DST, ORDER) \
197 if (sizeof *(DST) == 1 || sizeof *(DST) == 2 || sizeof *(DST) == 4) { \
198 atomic_readX(SRC, DST, ORDER) \
199 } else if (sizeof *(DST) == 8) { \
200 atomic_read64(SRC, DST, ORDER) \
201 } else { \
202 abort(); \
203 }
204
205 /* For add, sub, and logical operations, for 8, 16 and 64 bit data types,
206 * functions for all the different memory orders does not exist
207 * (though documentation exists for some of them). The MSVC C++ library which
208 * implements the c11 atomics simply calls the full memory barrier function
209 * for everything in x86(see xatomic.h). So do the same here. */
210
211 /* For 8, 16 and 64 bit variations. */
212 #define atomic_op(OP, X, RMW, ARG, ORIG, ORDER) \
213 atomic_##OP##_generic(X, RMW, ARG, ORIG, ORDER)
214
215 /* Arithmetic addition calls. */
216
217 #define atomic_add8(RMW, ARG, ORIG, ORDER) \
218 *(ORIG) = _InterlockedExchangeAdd8((char volatile *) (RMW), \
219 (char) (ARG));
220
221 #define atomic_add16(RMW, ARG, ORIG, ORDER) \
222 *(ORIG) = _InterlockedExchangeAdd16((short volatile *) (RMW), \
223 (short) (ARG));
224
225 #define atomic_add32(RMW, ARG, ORIG, ORDER) \
226 *(ORIG) = InterlockedExchangeAdd((long volatile *) (RMW), \
227 (long) (ARG));
228 #define atomic_add64(RMW, ARG, ORIG, ORDER) \
229 *(ORIG) = _InterlockedExchangeAdd64((int64_t volatile *) (RMW), \
230 (int64_t) (ARG));
231
232 #define atomic_add(RMW, ARG, ORIG) \
233 atomic_add_explicit(RMW, ARG, ORIG, memory_order_seq_cst)
234
235 #define atomic_add_explicit(RMW, ARG, ORIG, ORDER) \
236 if (sizeof *(RMW) == 1) { \
237 atomic_add8(RMW, ARG, ORIG, ORDER) \
238 } else if (sizeof *(RMW) == 2) { \
239 atomic_add16(RMW, ARG, ORIG, ORDER) \
240 } else if (sizeof *(RMW) == 4) { \
241 atomic_add32(RMW, ARG, ORIG, ORDER) \
242 } else if (sizeof *(RMW) == 8) { \
243 atomic_add64(RMW, ARG, ORIG, ORDER) \
244 } else { \
245 abort(); \
246 }
247
248 /* Arithmetic subtraction calls. */
249
250 #define atomic_sub(RMW, ARG, ORIG) \
251 atomic_add_explicit(RMW, (0 - (ARG)), ORIG, memory_order_seq_cst)
252
253 #define atomic_sub_explicit(RMW, ARG, ORIG, ORDER) \
254 atomic_add_explicit(RMW, (0 - (ARG)), ORIG, ORDER)
255
256 /* Logical 'and' calls. */
257
258 #define atomic_and32(RMW, ARG, ORIG, ORDER) \
259 *(ORIG) = InterlockedAnd((int32_t volatile *) (RMW), (int32_t) (ARG));
260
261 /* For 8, 16 and 64 bit variations. */
262 #define atomic_and_generic(X, RMW, ARG, ORIG, ORDER) \
263 *(ORIG) = InterlockedAnd##X((int##X##_t volatile *) (RMW), \
264 (int##X##_t) (ARG));
265
266 #define atomic_and(RMW, ARG, ORIG) \
267 atomic_and_explicit(RMW, ARG, ORIG, memory_order_seq_cst)
268
269 #define atomic_and_explicit(RMW, ARG, ORIG, ORDER) \
270 if (sizeof *(RMW) == 1) { \
271 atomic_op(and, 8, RMW, ARG, ORIG, ORDER) \
272 } else if (sizeof *(RMW) == 2) { \
273 atomic_op(and, 16, RMW, ARG, ORIG, ORDER) \
274 } else if (sizeof *(RMW) == 4) { \
275 atomic_and32(RMW, ARG, ORIG, ORDER) \
276 } else if (sizeof *(RMW) == 8) { \
277 atomic_op(and, 64, RMW, ARG, ORIG, ORDER) \
278 } else { \
279 abort(); \
280 }
281
282 /* Logical 'Or' calls. */
283
284 #define atomic_or32(RMW, ARG, ORIG, ORDER) \
285 *(ORIG) = InterlockedOr((int32_t volatile *) (RMW), (int32_t) (ARG));
286
287 /* For 8, 16 and 64 bit variations. */
288 #define atomic_or_generic(X, RMW, ARG, ORIG, ORDER) \
289 *(ORIG) = InterlockedOr##X((int##X##_t volatile *) (RMW), \
290 (int##X##_t) (ARG));
291
292 #define atomic_or(RMW, ARG, ORIG) \
293 atomic_or_explicit(RMW, ARG, ORIG, memory_order_seq_cst)
294
295 #define atomic_or_explicit(RMW, ARG, ORIG, ORDER) \
296 if (sizeof *(RMW) == 1) { \
297 atomic_op(or, 8, RMW, ARG, ORIG, ORDER) \
298 } else if (sizeof *(RMW) == 2) { \
299 atomic_op(or, 16, RMW, ARG, ORIG, ORDER) \
300 } else if (sizeof *(RMW) == 4) { \
301 atomic_or32(RMW, ARG, ORIG, ORDER) \
302 } else if (sizeof *(RMW) == 8) { \
303 atomic_op(or, 64, RMW, ARG, ORIG, ORDER) \
304 } else { \
305 abort(); \
306 }
307
308 /* Logical Xor calls. */
309
310 #define atomic_xor32(RMW, ARG, ORIG, ORDER) \
311 *(ORIG) = InterlockedXor((int32_t volatile *) (RMW), (int32_t) (ARG));
312
313 /* For 8, 16 and 64 bit variations. */
314 #define atomic_xor_generic(X, RMW, ARG, ORIG, ORDER) \
315 *(ORIG) = InterlockedXor##X((int##X##_t volatile *) (RMW), \
316 (int##X##_t) (ARG));
317
318 #define atomic_xor(RMW, ARG, ORIG) \
319 atomic_xor_explicit(RMW, ARG, ORIG, memory_order_seq_cst)
320
321 #define atomic_xor_explicit(RMW, ARG, ORIG, ORDER) \
322 if (sizeof *(RMW) == 1) { \
323 atomic_op(xor, 8, RMW, ARG, ORIG, ORDER) \
324 } else if (sizeof *(RMW) == 2) { \
325 atomic_op(xor, 16, RMW, ARG, ORIG, ORDER) \
326 } else if (sizeof *(RMW) == 4) { \
327 atomic_xor32(RMW, ARG, ORIG, ORDER); \
328 } else if (sizeof *(RMW) == 8) { \
329 atomic_op(xor, 64, RMW, ARG, ORIG, ORDER) \
330 } else { \
331 abort(); \
332 }
333
334 #define atomic_compare_exchange_strong(DST, EXP, SRC) \
335 atomic_compare_exchange_strong_explicit(DST, EXP, SRC, \
336 memory_order_seq_cst, \
337 memory_order_seq_cst)
338
339 #define atomic_compare_exchange_weak atomic_compare_exchange_strong
340 #define atomic_compare_exchange_weak_explicit \
341 atomic_compare_exchange_strong_explicit
342
343 /* MSVCs c++ compiler implements c11 atomics and looking through its
344 * implementation (in xatomic.h), orders are ignored for x86 platform.
345 * Do the same here. */
346 static inline bool
347 atomic_compare_exchange8(int8_t volatile *dst, int8_t *expected, int8_t src)
348 {
349 int8_t previous = _InterlockedCompareExchange8((char volatile *)dst,
350 src, *expected);
351 if (previous == *expected) {
352 return true;
353 } else {
354 *expected = previous;
355 return false;
356 }
357 }
358
359 static inline bool
360 atomic_compare_exchange16(int16_t volatile *dst, int16_t *expected,
361 int16_t src)
362 {
363 int16_t previous = InterlockedCompareExchange16(dst, src, *expected);
364 if (previous == *expected) {
365 return true;
366 } else {
367 *expected = previous;
368 return false;
369 }
370 }
371
372 static inline bool
373 atomic_compare_exchange32(int32_t volatile *dst, int32_t *expected,
374 int32_t src)
375 {
376 int32_t previous = InterlockedCompareExchange((long volatile *)dst,
377 src, *expected);
378 if (previous == *expected) {
379 return true;
380 } else {
381 *expected = previous;
382 return false;
383 }
384 }
385
386 static inline bool
387 atomic_compare_exchange64(int64_t volatile *dst, int64_t *expected,
388 int64_t src)
389 {
390 int64_t previous = InterlockedCompareExchange64(dst, src, *expected);
391 if (previous == *expected) {
392 return true;
393 } else {
394 *expected = previous;
395 return false;
396 }
397 }
398
399 static inline bool
400 atomic_compare_unreachable()
401 {
402 return true;
403 }
404
405 #define atomic_compare_exchange_strong_explicit(DST, EXP, SRC, ORD1, ORD2) \
406 (sizeof *(DST) == 1 \
407 ? atomic_compare_exchange8((int8_t volatile *) (DST), (int8_t *) (EXP), \
408 (int8_t) (SRC)) \
409 : (sizeof *(DST) == 2 \
410 ? atomic_compare_exchange16((int16_t volatile *) (DST), \
411 (int16_t *) (EXP), (int16_t) (SRC)) \
412 : (sizeof *(DST) == 4 \
413 ? atomic_compare_exchange32((int32_t volatile *) (DST), \
414 (int32_t *) (EXP), (int32_t) (SRC)) \
415 : (sizeof *(DST) == 8 \
416 ? atomic_compare_exchange64((int64_t volatile *) (DST), \
417 (int64_t *) (EXP), (int64_t) (SRC)) \
418 : ovs_fatal(0, "atomic operation with size greater than 8 bytes"), \
419 atomic_compare_unreachable()))))
420
421 \f
422 /* atomic_flag */
423
424 typedef ATOMIC(int32_t) atomic_flag;
425 #define ATOMIC_FLAG_INIT 0
426
427 #define atomic_flag_test_and_set(FLAG) \
428 (bool) InterlockedBitTestAndSet(FLAG, 0)
429
430 #define atomic_flag_test_and_set_explicit(FLAG, ORDER) \
431 atomic_flag_test_and_set(FLAG)
432
433 #define atomic_flag_clear_explicit(FLAG, ORDER) \
434 atomic_flag_clear()
435 #define atomic_flag_clear(FLAG) \
436 InterlockedBitTestAndReset(FLAG, 0)