FFmpeg
Loading...
Searching...
No Matches
stdatomic_impl.h
Go to the documentation of this file.
1/*
2 * This file is part of FFmpeg.
3 *
4 * FFmpeg is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * FFmpeg is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with FFmpeg; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19#ifndef COMPAT_ATOMICS_WIN32_STDATOMIC_IMPL_H
20#define COMPAT_ATOMICS_WIN32_STDATOMIC_IMPL_H
21
22#include <string.h>
23
24#include <intrin.h>
25
26#define FF_ATOMIC_LOCK_FREE 2
27
28/* x86 aligns the 8-byte automatic objects to 4 bytes, where an 8-byte access
29 * that crosses a cache line is not single-copy atomic. The typedefs carry
30 * the alignment, the qualifier form cannot. */
31#ifdef _M_IX86
32#define FF_ATOMIC_ALIGN64 __declspec(align(8))
33#else
34#define FF_ATOMIC_ALIGN64
35#endif
36
37/* Only compiler reordering has to be inhibited. */
39{
40 if (order != memory_order_relaxed)
41 _ReadWriteBarrier();
42}
43
44/*
45 * Everything is built on the compiler intrinsics, which are available for every
46 * target and build type. SDK helpers are not very helpful here, as things are
47 * just not available in some configurations. The plain accesses go through the
48 * __iso_volatile intrinsics, which the compiler lowers to a single atomic
49 * access of the width W for every naturally aligned object, including the
50 * 8-byte ones on x86. The interlocked intrinsics are full barriers on x86
51 * and x64, the ARM targets have acquire, release and no fence variants of
52 * them.
53 */
54#if defined(_M_ARM64) || defined(_M_ARM64EC)
55
56/*
57 * The load-acquire and store-release instructions are sequentially consistent
58 * among themselves, so no access needs a barrier. This relies on every acquire
59 * load being the RCsc ldar, the RCpc ldapr of __load_acquire would need a
60 * barrier after the seq_cst stores. ARM64EC compiles to the same code with the
61 * x64 ABI.
62 */
63#define WIN32_ATOMIC_LOAD_STORE(name, type, W) \
64static av_always_inline type \
65ff_atomic_load_##name(const volatile void *object, memory_order order) \
66{ \
67 if (order != memory_order_relaxed) \
68 return (type)__ldar##W((unsigned __int##W volatile *)object); \
69 return (type)__iso_volatile_load##W((const volatile __int##W *)object); \
70} \
71 \
72static av_always_inline void ff_atomic_store_##name(volatile void *object, \
73 type desired, \
74 memory_order order) \
75{ \
76 if (order == memory_order_relaxed) \
77 __iso_volatile_store##W((volatile __int##W *)object, \
78 (__int##W)desired); \
79 else \
80 __stlr##W((unsigned __int##W volatile *)object, \
81 (unsigned __int##W)desired); \
82}
83
84#define WIN32_ATOMIC_ORDERED(Intrinsic, suffix) Intrinsic##suffix
85#define WIN32_ATOMIC_XCHG_REL _rel
86
88{
89 if (order == memory_order_acquire || order == memory_order_consume)
90 __dmb(_ARM64_BARRIER_ISHLD);
91 else if (order != memory_order_relaxed)
92 __dmb(_ARM64_BARRIER_ISH);
93}
94
95#elif defined(_M_ARM)
96
97/*
98 * The plain accesses need explicit barriers, the inner shareable one after an
99 * acquire load and before a release store, on both sides of a seq_cst store.
100 * The exchange has no documented release variant, the full one serves. The
101 * 8-byte plain accesses are not single-copy atomic without LPAE, the 64-bit
102 * load and store are the exclusive ones.
103 */
104#define WIN32_ATOMIC_LOAD_STORE(name, type, W) \
105static av_always_inline type \
106ff_atomic_load_##name(const volatile void *object, memory_order order) \
107{ \
108 type value = \
109 (type)__iso_volatile_load##W((const volatile __int##W *)object); \
110 if (order != memory_order_relaxed) \
111 __dmb(_ARM_BARRIER_ISH); \
112 return value; \
113} \
114 \
115static av_always_inline void ff_atomic_store_##name(volatile void *object, \
116 type desired, \
117 memory_order order) \
118{ \
119 if (order != memory_order_relaxed) \
120 __dmb(_ARM_BARRIER_ISH); \
121 __iso_volatile_store##W((volatile __int##W *)object, (__int##W)desired); \
122 if (order == memory_order_seq_cst) \
123 __dmb(_ARM_BARRIER_ISH); \
124}
125
126#define WIN32_ATOMIC_ORDERED(Intrinsic, suffix) Intrinsic##suffix
127#define WIN32_ATOMIC_XCHG_REL
128
130{
131 if (order != memory_order_relaxed)
132 __dmb(_ARM_BARRIER_ISH);
133}
134
135#else
136
137/*
138 * x86 and x64 are strongly ordered, only the compiler has to be kept from
139 * reordering, and only the seq_cst store needs the full barrier after it.
140 * A locked instruction is the cheaper full barrier.
141 */
143{
144#ifdef _M_X64
145 __faststorefence();
146#else
147 long barrier;
148 _InterlockedOr(&barrier, 0);
149#endif
150}
151
152#define WIN32_ATOMIC_LOAD_STORE(name, type, W) \
153static av_always_inline type \
154ff_atomic_load_##name(const volatile void *object, memory_order order) \
155{ \
156 type value = \
157 (type)__iso_volatile_load##W((const volatile __int##W *)object); \
158 if (order != memory_order_relaxed) \
159 _ReadWriteBarrier(); \
160 return value; \
161} \
162 \
163static av_always_inline void ff_atomic_store_##name(volatile void *object, \
164 type desired, \
165 memory_order order) \
166{ \
167 if (order != memory_order_relaxed) \
168 _ReadWriteBarrier(); \
169 __iso_volatile_store##W((volatile __int##W *)object, (__int##W)desired); \
170 if (order == memory_order_seq_cst) \
171 win32_atomic_full_fence(); \
172}
173
174#define WIN32_ATOMIC_ORDERED(Intrinsic, suffix) Intrinsic
175#define WIN32_ATOMIC_XCHG_REL
176
178{
179 if (order == memory_order_seq_cst)
181 else if (order != memory_order_relaxed)
182 _ReadWriteBarrier();
183}
184
185#endif
186
187#define WIN32_ATOMIC_CALL(Intrinsic, rel, order, ...) \
188 ((order) == memory_order_relaxed \
189 ? WIN32_ATOMIC_ORDERED(Intrinsic, _nf)(__VA_ARGS__) \
190 : (order) == memory_order_release \
191 ? WIN32_ATOMIC_ORDERED(Intrinsic, rel)(__VA_ARGS__) \
192 : (order) == memory_order_acquire || (order) == memory_order_consume \
193 ? WIN32_ATOMIC_ORDERED(Intrinsic, _acq)(__VA_ARGS__) \
194 : Intrinsic(__VA_ARGS__))
195
196/*
197 * x86 has no 64-bit interlocked intrinsics besides the compare exchange, the
198 * others are compare exchange loops. The SDK provides such loops, but they
199 * do the arithmetic in signed types, so the wraparound is undefined behaviour
200 * there.
201 */
202#ifdef _M_IX86
203#define WIN32_ATOMIC_CAS_LOOP64(name, expr) \
204static av_always_inline long long \
205win32_Interlocked##name##64(volatile long long *object, long long operand) \
206{ \
207 long long old = __iso_volatile_load64(object), prev; \
208 while ((prev = _InterlockedCompareExchange64(object, (long long)(expr), \
209 old)) != old) \
210 old = prev; \
211 return old; \
212}
213WIN32_ATOMIC_CAS_LOOP64(Exchange, operand)
214WIN32_ATOMIC_CAS_LOOP64(ExchangeAdd, (uint64_t)old + (uint64_t)operand)
215WIN32_ATOMIC_CAS_LOOP64(Or, old | operand)
216WIN32_ATOMIC_CAS_LOOP64(Xor, old ^ operand)
217WIN32_ATOMIC_CAS_LOOP64(And, old & operand)
218#undef WIN32_ATOMIC_CAS_LOOP64
219#define win32_InterlockedCompareExchange64 _InterlockedCompareExchange64
220#endif
221
222#define WIN32_ATOMIC_OP(op, Intrinsic, rel, expr, name, type) \
223static av_always_inline type ff_atomic_##op##name(volatile void *object, \
224 type operand, \
225 memory_order order) \
226{ \
227 return WIN32_ATOMIC_CALL(Intrinsic, rel, order, object, expr); \
228}
229
230/* P and S are the prefix and the width suffix of the intrinsics. The
231 * expected value is copied instead of accessed through a pointer of the
232 * helper type, its object has the type of the caller. */
233#define WIN32_ATOMIC_RMW(name, type, P, S) \
234static av_always_inline bool ff_atomic_cas_##name(volatile void *object, \
235 void *expected, \
236 type desired, \
237 memory_order order) \
238{ \
239 type old, prev; \
240 memcpy(&old, expected, sizeof(old)); \
241 prev = WIN32_ATOMIC_CALL(P##CompareExchange##S, _rel, order, \
242 object, desired, old); \
243 if (prev == old) \
244 return 1; \
245 memcpy(expected, &prev, sizeof(prev)); \
246 return 0; \
247} \
248 \
249WIN32_ATOMIC_OP(exchange, P##Exchange##S, WIN32_ATOMIC_XCHG_REL, \
250 operand, _##name, type) \
251WIN32_ATOMIC_OP(fetch_add, P##ExchangeAdd##S, _rel, operand, _##name, type) \
252WIN32_ATOMIC_OP(fetch_sub, P##ExchangeAdd##S, _rel, \
253 (type)(0 - (uint64_t)operand), _##name, type) \
254WIN32_ATOMIC_OP(fetch_or, P##Or##S, _rel, operand, _##name, type) \
255WIN32_ATOMIC_OP(fetch_xor, P##Xor##S, _rel, operand, _##name, type) \
256WIN32_ATOMIC_OP(fetch_and, P##And##S, _rel, operand, _##name, type)
257
258/* bool objects get their own set, for normalization */
259WIN32_ATOMIC_LOAD_STORE(bool, bool, 8)
260WIN32_ATOMIC_LOAD_STORE(8, unsigned char, 8)
261WIN32_ATOMIC_LOAD_STORE(16, unsigned short, 16)
262WIN32_ATOMIC_LOAD_STORE(32, unsigned long, 32)
263#ifndef _M_ARM
264WIN32_ATOMIC_LOAD_STORE(64, unsigned long long, 64)
265#else
266static av_always_inline unsigned long long
267ff_atomic_load_64(const volatile void *object, memory_order order)
268{
269 unsigned long long value = __ldrexd((const volatile __int64 *)object);
270 if (order != memory_order_relaxed)
271 __dmb(_ARM_BARRIER_ISH);
272 return value;
273}
274
275static av_always_inline void ff_atomic_store_64(volatile void *object,
276 unsigned long long desired,
277 memory_order order)
278{
279 if (order == memory_order_relaxed)
280 _InterlockedExchange64_nf((volatile long long *)object, desired);
281 else
282 _InterlockedExchange64((volatile long long *)object, desired);
283}
284#endif
285
286WIN32_ATOMIC_RMW(bool, bool, _Interlocked, 8)
287WIN32_ATOMIC_RMW(8, unsigned char, _Interlocked, 8)
288WIN32_ATOMIC_RMW(16, unsigned short, _Interlocked, 16)
289WIN32_ATOMIC_RMW(32, unsigned long, _Interlocked, )
290#ifdef _M_IX86
291WIN32_ATOMIC_RMW(64, unsigned long long, win32_Interlocked, 64)
292#else
293WIN32_ATOMIC_RMW(64, unsigned long long, _Interlocked, 64)
294#endif
295
296#undef WIN32_ATOMIC_LOAD_STORE
297#undef WIN32_ATOMIC_RMW
298#undef WIN32_ATOMIC_OP
299#undef WIN32_ATOMIC_CALL
300#undef WIN32_ATOMIC_ORDERED
301#undef WIN32_ATOMIC_XCHG_REL
302#undef win32_InterlockedCompareExchange64
303
304#endif /* COMPAT_ATOMICS_WIN32_STDATOMIC_IMPL_H */
#define ff_atomic_thread_fence(order)
#define ff_atomic_signal_fence(order)
double value
Definition eval.c:102
#define av_always_inline
Definition attributes.h:72
memory_order
Definition stdatomic.h:28
@ memory_order_consume
Definition stdatomic.h:30
@ memory_order_seq_cst
Definition stdatomic.h:34
@ memory_order_relaxed
Definition stdatomic.h:29
@ memory_order_acquire
Definition stdatomic.h:31
static av_always_inline void win32_atomic_full_fence(void)
#define WIN32_ATOMIC_RMW(name, type, P, S)
#define WIN32_ATOMIC_LOAD_STORE(name, type, W)