Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/sdl/atomic/SDL_atomic.c
9904 views
1
/*
2
Simple DirectMedia Layer
3
Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
4
5
This software is provided 'as-is', without any express or implied
6
warranty. In no event will the authors be held liable for any damages
7
arising from the use of this software.
8
9
Permission is granted to anyone to use this software for any purpose,
10
including commercial applications, and to alter it and redistribute it
11
freely, subject to the following restrictions:
12
13
1. The origin of this software must not be misrepresented; you must not
14
claim that you wrote the original software. If you use this software
15
in a product, an acknowledgment in the product documentation would be
16
appreciated but is not required.
17
2. Altered source versions must be plainly marked as such, and must not be
18
misrepresented as being the original software.
19
3. This notice may not be removed or altered from any source distribution.
20
*/
21
#include "SDL_internal.h"
22
23
#if defined(_MSC_VER) && (_MSC_VER >= 1900)
24
#include <intrin.h>
25
#define HAVE_MSC_ATOMICS 1
26
#endif
27
28
#ifdef SDL_PLATFORM_MACOS // !!! FIXME: should we favor gcc atomics?
29
#include <libkern/OSAtomic.h>
30
#endif
31
32
#if !defined(HAVE_GCC_ATOMICS) && defined(SDL_PLATFORM_SOLARIS)
33
#include <atomic.h>
34
#endif
35
36
// The __atomic_load_n() intrinsic showed up in different times for different compilers.
37
#ifdef __clang__
38
#if __has_builtin(__atomic_load_n) || defined(HAVE_GCC_ATOMICS)
39
/* !!! FIXME: this advertises as available in the NDK but uses an external symbol we don't have.
40
It might be in a later NDK or we might need an extra library? --ryan. */
41
#ifndef SDL_PLATFORM_ANDROID
42
#define HAVE_ATOMIC_LOAD_N 1
43
#endif
44
#endif
45
#elif defined(__GNUC__)
46
#if (__GNUC__ >= 5)
47
#define HAVE_ATOMIC_LOAD_N 1
48
#endif
49
#endif
50
51
/* *INDENT-OFF* */ // clang-format off
52
#if defined(__WATCOMC__) && defined(__386__)
53
SDL_COMPILE_TIME_ASSERT(intsize, 4==sizeof(int));
54
#define HAVE_WATCOM_ATOMICS
55
extern __inline int _SDL_xchg_watcom(volatile int *a, int v);
56
#pragma aux _SDL_xchg_watcom = \
57
"lock xchg [ecx], eax" \
58
parm [ecx] [eax] \
59
value [eax] \
60
modify exact [eax];
61
62
extern __inline unsigned char _SDL_cmpxchg_watcom(volatile int *a, int newval, int oldval);
63
#pragma aux _SDL_cmpxchg_watcom = \
64
"lock cmpxchg [edx], ecx" \
65
"setz al" \
66
parm [edx] [ecx] [eax] \
67
value [al] \
68
modify exact [eax];
69
70
extern __inline int _SDL_xadd_watcom(volatile int *a, int v);
71
#pragma aux _SDL_xadd_watcom = \
72
"lock xadd [ecx], eax" \
73
parm [ecx] [eax] \
74
value [eax] \
75
modify exact [eax];
76
77
#endif // __WATCOMC__ && __386__
78
/* *INDENT-ON* */ // clang-format on
79
80
/*
81
If any of the operations are not provided then we must emulate some
82
of them. That means we need a nice implementation of spin locks
83
that avoids the "one big lock" problem. We use a vector of spin
84
locks and pick which one to use based on the address of the operand
85
of the function.
86
87
To generate the index of the lock we first shift by 3 bits to get
88
rid on the zero bits that result from 32 and 64 bit alignment of
89
data. We then mask off all but 5 bits and use those 5 bits as an
90
index into the table.
91
92
Picking the lock this way insures that accesses to the same data at
93
the same time will go to the same lock. OTOH, accesses to different
94
data have only a 1/32 chance of hitting the same lock. That should
95
pretty much eliminate the chances of several atomic operations on
96
different data from waiting on the same "big lock". If it isn't
97
then the table of locks can be expanded to a new size so long as
98
the new size is a power of two.
99
100
Contributed by Bob Pendleton, [email protected]
101
*/
102
103
#if !defined(HAVE_MSC_ATOMICS) && !defined(HAVE_GCC_ATOMICS) && !defined(SDL_PLATFORM_MACOS) && !defined(SDL_PLATFORM_SOLARIS) && !defined(HAVE_WATCOM_ATOMICS)
104
#define EMULATE_CAS 1
105
#endif
106
107
#ifdef EMULATE_CAS
108
static SDL_SpinLock locks[32];
109
110
static SDL_INLINE void enterLock(void *a)
111
{
112
uintptr_t index = ((((uintptr_t)a) >> 3) & 0x1f);
113
114
SDL_LockSpinlock(&locks[index]);
115
}
116
117
static SDL_INLINE void leaveLock(void *a)
118
{
119
uintptr_t index = ((((uintptr_t)a) >> 3) & 0x1f);
120
121
SDL_UnlockSpinlock(&locks[index]);
122
}
123
#endif
124
125
bool SDL_CompareAndSwapAtomicInt(SDL_AtomicInt *a, int oldval, int newval)
126
{
127
#ifdef HAVE_MSC_ATOMICS
128
SDL_COMPILE_TIME_ASSERT(atomic_cas, sizeof(long) == sizeof(a->value));
129
return _InterlockedCompareExchange((long *)&a->value, (long)newval, (long)oldval) == (long)oldval;
130
#elif defined(HAVE_WATCOM_ATOMICS)
131
return _SDL_cmpxchg_watcom((volatile int *)&a->value, newval, oldval);
132
#elif defined(HAVE_GCC_ATOMICS)
133
return __sync_bool_compare_and_swap(&a->value, oldval, newval);
134
#elif defined(SDL_PLATFORM_MACOS) // this is deprecated in 10.12 sdk; favor gcc atomics.
135
return OSAtomicCompareAndSwap32Barrier(oldval, newval, &a->value);
136
#elif defined(SDL_PLATFORM_SOLARIS)
137
SDL_COMPILE_TIME_ASSERT(atomic_cas, sizeof(uint_t) == sizeof(a->value));
138
return ((int)atomic_cas_uint((volatile uint_t *)&a->value, (uint_t)oldval, (uint_t)newval) == oldval);
139
#elif defined(EMULATE_CAS)
140
bool result = false;
141
142
enterLock(a);
143
if (a->value == oldval) {
144
a->value = newval;
145
result = true;
146
}
147
leaveLock(a);
148
149
return result;
150
#else
151
#error Please define your platform.
152
#endif
153
}
154
155
bool SDL_CompareAndSwapAtomicU32(SDL_AtomicU32 *a, Uint32 oldval, Uint32 newval)
156
{
157
#ifdef HAVE_MSC_ATOMICS
158
SDL_COMPILE_TIME_ASSERT(atomic_cas, sizeof(long) == sizeof(a->value));
159
return _InterlockedCompareExchange((long *)&a->value, (long)newval, (long)oldval) == (long)oldval;
160
#elif defined(HAVE_WATCOM_ATOMICS)
161
SDL_COMPILE_TIME_ASSERT(atomic_cas, sizeof(int) == sizeof(a->value));
162
return _SDL_cmpxchg_watcom((volatile int *)&a->value, (int)newval, (int)oldval);
163
#elif defined(HAVE_GCC_ATOMICS)
164
return __sync_bool_compare_and_swap(&a->value, oldval, newval);
165
#elif defined(SDL_PLATFORM_MACOS) // this is deprecated in 10.12 sdk; favor gcc atomics.
166
return OSAtomicCompareAndSwap32Barrier((int32_t)oldval, (int32_t)newval, (int32_t*)&a->value);
167
#elif defined(SDL_PLATFORM_SOLARIS)
168
SDL_COMPILE_TIME_ASSERT(atomic_cas, sizeof(uint_t) == sizeof(a->value));
169
return ((Uint32)atomic_cas_uint((volatile uint_t *)&a->value, (uint_t)oldval, (uint_t)newval) == oldval);
170
#elif defined(EMULATE_CAS)
171
bool result = false;
172
173
enterLock(a);
174
if (a->value == oldval) {
175
a->value = newval;
176
result = true;
177
}
178
leaveLock(a);
179
180
return result;
181
#else
182
#error Please define your platform.
183
#endif
184
}
185
186
bool SDL_CompareAndSwapAtomicPointer(void **a, void *oldval, void *newval)
187
{
188
#ifdef HAVE_MSC_ATOMICS
189
return _InterlockedCompareExchangePointer(a, newval, oldval) == oldval;
190
#elif defined(HAVE_WATCOM_ATOMICS)
191
return _SDL_cmpxchg_watcom((int *)a, (long)newval, (long)oldval);
192
#elif defined(HAVE_GCC_ATOMICS)
193
return __sync_bool_compare_and_swap(a, oldval, newval);
194
#elif defined(SDL_PLATFORM_MACOS) && defined(__LP64__) // this is deprecated in 10.12 sdk; favor gcc atomics.
195
return OSAtomicCompareAndSwap64Barrier((int64_t)oldval, (int64_t)newval, (int64_t *)a);
196
#elif defined(SDL_PLATFORM_MACOS) && !defined(__LP64__) // this is deprecated in 10.12 sdk; favor gcc atomics.
197
return OSAtomicCompareAndSwap32Barrier((int32_t)oldval, (int32_t)newval, (int32_t *)a);
198
#elif defined(SDL_PLATFORM_SOLARIS)
199
return (atomic_cas_ptr(a, oldval, newval) == oldval);
200
#elif defined(EMULATE_CAS)
201
bool result = false;
202
203
enterLock(a);
204
if (*a == oldval) {
205
*a = newval;
206
result = true;
207
}
208
leaveLock(a);
209
210
return result;
211
#else
212
#error Please define your platform.
213
#endif
214
}
215
216
int SDL_SetAtomicInt(SDL_AtomicInt *a, int v)
217
{
218
#ifdef HAVE_MSC_ATOMICS
219
SDL_COMPILE_TIME_ASSERT(atomic_set, sizeof(long) == sizeof(a->value));
220
return _InterlockedExchange((long *)&a->value, v);
221
#elif defined(HAVE_WATCOM_ATOMICS)
222
return _SDL_xchg_watcom(&a->value, v);
223
#elif defined(HAVE_GCC_ATOMICS)
224
return __sync_lock_test_and_set(&a->value, v);
225
#elif defined(SDL_PLATFORM_SOLARIS)
226
SDL_COMPILE_TIME_ASSERT(atomic_set, sizeof(uint_t) == sizeof(a->value));
227
return (int)atomic_swap_uint((volatile uint_t *)&a->value, v);
228
#else
229
int value;
230
do {
231
value = a->value;
232
} while (!SDL_CompareAndSwapAtomicInt(a, value, v));
233
return value;
234
#endif
235
}
236
237
Uint32 SDL_SetAtomicU32(SDL_AtomicU32 *a, Uint32 v)
238
{
239
#ifdef HAVE_MSC_ATOMICS
240
SDL_COMPILE_TIME_ASSERT(atomic_set, sizeof(long) == sizeof(a->value));
241
return _InterlockedExchange((long *)&a->value, v);
242
#elif defined(HAVE_WATCOM_ATOMICS)
243
return _SDL_xchg_watcom(&a->value, v);
244
#elif defined(HAVE_GCC_ATOMICS)
245
return __sync_lock_test_and_set(&a->value, v);
246
#elif defined(SDL_PLATFORM_SOLARIS)
247
SDL_COMPILE_TIME_ASSERT(atomic_set, sizeof(uint_t) == sizeof(a->value));
248
return (Uint32)atomic_swap_uint((volatile uint_t *)&a->value, v);
249
#else
250
Uint32 value;
251
do {
252
value = a->value;
253
} while (!SDL_CompareAndSwapAtomicU32(a, value, v));
254
return value;
255
#endif
256
}
257
258
void *SDL_SetAtomicPointer(void **a, void *v)
259
{
260
#ifdef HAVE_MSC_ATOMICS
261
return _InterlockedExchangePointer(a, v);
262
#elif defined(HAVE_WATCOM_ATOMICS)
263
return (void *)_SDL_xchg_watcom((int *)a, (long)v);
264
#elif defined(HAVE_GCC_ATOMICS)
265
return __sync_lock_test_and_set(a, v);
266
#elif defined(SDL_PLATFORM_SOLARIS)
267
return atomic_swap_ptr(a, v);
268
#else
269
void *value;
270
do {
271
value = *a;
272
} while (!SDL_CompareAndSwapAtomicPointer(a, value, v));
273
return value;
274
#endif
275
}
276
277
int SDL_AddAtomicInt(SDL_AtomicInt *a, int v)
278
{
279
#ifdef HAVE_MSC_ATOMICS
280
SDL_COMPILE_TIME_ASSERT(atomic_add, sizeof(long) == sizeof(a->value));
281
return _InterlockedExchangeAdd((long *)&a->value, v);
282
#elif defined(HAVE_WATCOM_ATOMICS)
283
SDL_COMPILE_TIME_ASSERT(atomic_add, sizeof(int) == sizeof(a->value));
284
return _SDL_xadd_watcom((volatile int *)&a->value, v);
285
#elif defined(HAVE_GCC_ATOMICS)
286
return __sync_fetch_and_add(&a->value, v);
287
#elif defined(SDL_PLATFORM_SOLARIS)
288
int pv = a->value;
289
membar_consumer();
290
atomic_add_int((volatile uint_t *)&a->value, v);
291
return pv;
292
#else
293
int value;
294
do {
295
value = a->value;
296
} while (!SDL_CompareAndSwapAtomicInt(a, value, (value + v)));
297
return value;
298
#endif
299
}
300
301
int SDL_GetAtomicInt(SDL_AtomicInt *a)
302
{
303
#ifdef HAVE_ATOMIC_LOAD_N
304
return __atomic_load_n(&a->value, __ATOMIC_SEQ_CST);
305
#elif defined(HAVE_MSC_ATOMICS)
306
SDL_COMPILE_TIME_ASSERT(atomic_get, sizeof(long) == sizeof(a->value));
307
return _InterlockedOr((long *)&a->value, 0);
308
#elif defined(HAVE_WATCOM_ATOMICS)
309
return _SDL_xadd_watcom(&a->value, 0);
310
#elif defined(HAVE_GCC_ATOMICS)
311
return __sync_or_and_fetch(&a->value, 0);
312
#elif defined(SDL_PLATFORM_MACOS) // this is deprecated in 10.12 sdk; favor gcc atomics.
313
return sizeof(a->value) == sizeof(uint32_t) ? OSAtomicOr32Barrier(0, (volatile uint32_t *)&a->value) : OSAtomicAdd64Barrier(0, (volatile int64_t *)&a->value);
314
#elif defined(SDL_PLATFORM_SOLARIS)
315
return atomic_or_uint_nv((volatile uint_t *)&a->value, 0);
316
#else
317
int value;
318
do {
319
value = a->value;
320
} while (!SDL_CompareAndSwapAtomicInt(a, value, value));
321
return value;
322
#endif
323
}
324
325
Uint32 SDL_GetAtomicU32(SDL_AtomicU32 *a)
326
{
327
#ifdef HAVE_ATOMIC_LOAD_N
328
return __atomic_load_n(&a->value, __ATOMIC_SEQ_CST);
329
#elif defined(HAVE_MSC_ATOMICS)
330
SDL_COMPILE_TIME_ASSERT(atomic_get, sizeof(long) == sizeof(a->value));
331
return (Uint32)_InterlockedOr((long *)&a->value, 0);
332
#elif defined(HAVE_WATCOM_ATOMICS)
333
SDL_COMPILE_TIME_ASSERT(atomic_get, sizeof(int) == sizeof(a->value));
334
return (Uint32)_SDL_xadd_watcom((volatile int *)&a->value, 0);
335
#elif defined(HAVE_GCC_ATOMICS)
336
return __sync_or_and_fetch(&a->value, 0);
337
#elif defined(SDL_PLATFORM_MACOS) // this is deprecated in 10.12 sdk; favor gcc atomics.
338
return OSAtomicOr32Barrier(0, (volatile uint32_t *)&a->value);
339
#elif defined(SDL_PLATFORM_SOLARIS)
340
SDL_COMPILE_TIME_ASSERT(atomic_get, sizeof(uint_t) == sizeof(a->value));
341
return (Uint32)atomic_or_uint_nv((volatile uint_t *)&a->value, 0);
342
#else
343
Uint32 value;
344
do {
345
value = a->value;
346
} while (!SDL_CompareAndSwapAtomicU32(a, value, value));
347
return value;
348
#endif
349
}
350
351
void *SDL_GetAtomicPointer(void **a)
352
{
353
#ifdef HAVE_ATOMIC_LOAD_N
354
return __atomic_load_n(a, __ATOMIC_SEQ_CST);
355
#elif defined(HAVE_MSC_ATOMICS)
356
return _InterlockedCompareExchangePointer(a, NULL, NULL);
357
#elif defined(HAVE_GCC_ATOMICS)
358
return __sync_val_compare_and_swap(a, (void *)0, (void *)0);
359
#elif defined(SDL_PLATFORM_SOLARIS)
360
return atomic_cas_ptr(a, (void *)0, (void *)0);
361
#else
362
void *value;
363
do {
364
value = *a;
365
} while (!SDL_CompareAndSwapAtomicPointer(a, value, value));
366
return value;
367
#endif
368
}
369
370
#ifdef SDL_MEMORY_BARRIER_USES_FUNCTION
371
#error This file should be built in arm mode so the mcr instruction is available for memory barriers
372
#endif
373
374
void SDL_MemoryBarrierReleaseFunction(void)
375
{
376
SDL_MemoryBarrierRelease();
377
}
378
379
void SDL_MemoryBarrierAcquireFunction(void)
380
{
381
SDL_MemoryBarrierAcquire();
382
}
383
384