SDL  2.0
SDL_atomic.h File Reference
#include "SDL_stdinc.h"
#include "SDL_platform.h"
#include "begin_code.h"
#include "close_code.h"
+ Include dependency graph for SDL_atomic.h:
+ This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Data Structures

struct  SDL_atomic_t
 A type representing an atomic integer value. It is a struct so people don't accidentally use numeric operations on it. More...
 

SDL AtomicLock

The atomic locks are efficient spinlocks using CPU instructions, but are vulnerable to starvation and can spin forever if a thread holding a lock has been terminated. For this reason you should minimize the code executed inside an atomic lock and never do expensive things like API or system calls while holding them.

The atomic locks are not safe to lock recursively.

Porting Note: The spin lock functions and type are required and can not be emulated because they are used in the atomic emulation code.

#define SDL_CompilerBarrier()   { SDL_SpinLock _tmp = 0; SDL_AtomicLock(&_tmp); SDL_AtomicUnlock(&_tmp); }
 
#define SDL_MemoryBarrierRelease()   SDL_CompilerBarrier()
 
#define SDL_MemoryBarrierAcquire()   SDL_CompilerBarrier()
 
#define SDL_AtomicIncRef(a)   SDL_AtomicAdd(a, 1)
 Increment an atomic variable used as a reference count. More...
 
#define SDL_AtomicDecRef(a)   (SDL_AtomicAdd(a, -1) == 1)
 Decrement an atomic variable used as a reference count. More...
 
typedef int SDL_SpinLock
 
SDL_bool SDL_AtomicTryLock (SDL_SpinLock *lock)
 Try to lock a spin lock by setting it to a non-zero value. More...
 
void SDL_AtomicLock (SDL_SpinLock *lock)
 Lock a spin lock by setting it to a non-zero value. More...
 
void SDL_AtomicUnlock (SDL_SpinLock *lock)
 Unlock a spin lock by setting it to 0. Always returns immediately. More...
 
void SDL_MemoryBarrierReleaseFunction (void)
 
void SDL_MemoryBarrierAcquireFunction (void)
 
SDL_bool SDL_AtomicCAS (SDL_atomic_t *a, int oldval, int newval)
 Set an atomic variable to a new value if it is currently an old value. More...
 
int SDL_AtomicSet (SDL_atomic_t *a, int v)
 Set an atomic variable to a value. More...
 
int SDL_AtomicGet (SDL_atomic_t *a)
 Get the value of an atomic variable. More...
 
int SDL_AtomicAdd (SDL_atomic_t *a, int v)
 Add to an atomic variable. More...
 
SDL_bool SDL_AtomicCASPtr (void **a, void *oldval, void *newval)
 Set a pointer to a new value if it is currently an old value. More...
 
voidSDL_AtomicSetPtr (void **a, void *v)
 Set a pointer to a value atomically. More...
 
voidSDL_AtomicGetPtr (void **a)
 Get the value of a pointer atomically. More...
 

Detailed Description

Atomic operations.

IMPORTANT: If you are not an expert in concurrent lockless programming, you should only be using the atomic lock and reference counting functions in this file. In all other cases you should be protecting your data structures with full mutexes.

The list of "safe" functions to use are: SDL_AtomicLock() SDL_AtomicUnlock() SDL_AtomicIncRef() SDL_AtomicDecRef()

Seriously, here be dragons! ^^^^^^^^^^^^^^^^^^^^^^^^^^^

You can find out a little more about lockless programming and the subtle issues that can arise here: http://msdn.microsoft.com/en-us/library/ee418650%28v=vs.85%29.aspx

There's also lots of good information here: http://www.1024cores.net/home/lock-free-algorithms http://preshing.com/

These operations may or may not actually be implemented using processor specific atomic operations. When possible they are implemented as true processor specific atomic operations. When that is not possible the are implemented using locks that do use the available atomic operations.

All of the atomic operations that modify memory are full memory barriers.

Definition in file SDL_atomic.h.

Macro Definition Documentation

◆ SDL_AtomicDecRef

#define SDL_AtomicDecRef (   a)    (SDL_AtomicAdd(a, -1) == 1)

Decrement an atomic variable used as a reference count.

Returns
SDL_TRUE if the variable reached zero after decrementing, SDL_FALSE otherwise

Definition at line 263 of file SDL_atomic.h.

◆ SDL_AtomicIncRef

#define SDL_AtomicIncRef (   a)    SDL_AtomicAdd(a, 1)

Increment an atomic variable used as a reference count.

Definition at line 253 of file SDL_atomic.h.

◆ SDL_CompilerBarrier

#define SDL_CompilerBarrier ( )    { SDL_SpinLock _tmp = 0; SDL_AtomicLock(&_tmp); SDL_AtomicUnlock(&_tmp); }

The compiler barrier prevents the compiler from reordering reads and writes to globally visible variables across the call.

Definition at line 133 of file SDL_atomic.h.

◆ SDL_MemoryBarrierAcquire

#define SDL_MemoryBarrierAcquire ( )    SDL_CompilerBarrier()

Definition at line 209 of file SDL_atomic.h.

◆ SDL_MemoryBarrierRelease

#define SDL_MemoryBarrierRelease ( )    SDL_CompilerBarrier()

Definition at line 208 of file SDL_atomic.h.

Typedef Documentation

◆ SDL_SpinLock

typedef int SDL_SpinLock

Definition at line 89 of file SDL_atomic.h.

Function Documentation

◆ SDL_AtomicAdd()

int SDL_AtomicAdd ( SDL_atomic_t a,
int  v 
)

Add to an atomic variable.

Returns
The previous value of the atomic variable.
Note
This same style can be used for any number operation

Definition at line 238 of file SDL_atomic.c.

239 {
240 #ifdef HAVE_MSC_ATOMICS
241  return _InterlockedExchangeAdd((long*)&a->value, v);
242 #elif defined(HAVE_WATCOM_ATOMICS)
243  return _SDL_xadd_watcom(&a->value, v);
244 #elif defined(HAVE_GCC_ATOMICS)
245  return __sync_fetch_and_add(&a->value, v);
246 #elif defined(__SOLARIS__)
247  int pv = a->value;
248  membar_consumer();
249 #if defined(_LP64)
250  atomic_add_64((volatile uint64_t*)&a->value, v);
251 #elif !defined(_LP64)
252  atomic_add_32((volatile uint32_t*)&a->value, v);
253 #endif
254  return pv;
255 #else
256  int value;
257  do {
258  value = a->value;
259  } while (!SDL_AtomicCAS(a, value, (value + v)));
260  return value;
261 #endif
262 }

References SDL_AtomicCAS().

◆ SDL_AtomicCAS()

SDL_bool SDL_AtomicCAS ( SDL_atomic_t a,
int  oldval,
int  newval 
)

Set an atomic variable to a new value if it is currently an old value.

Returns
SDL_TRUE if the atomic variable was set, SDL_FALSE otherwise.
Note
If you don't know what this function is for, you shouldn't use it!

Definition at line 130 of file SDL_atomic.c.

131 {
132 #ifdef HAVE_MSC_ATOMICS
133  return (_InterlockedCompareExchange((long*)&a->value, (long)newval, (long)oldval) == (long)oldval);
134 #elif defined(HAVE_WATCOM_ATOMICS)
135  return (SDL_bool) _SDL_cmpxchg_watcom(&a->value, newval, oldval);
136 #elif defined(HAVE_GCC_ATOMICS)
137  return (SDL_bool) __sync_bool_compare_and_swap(&a->value, oldval, newval);
138 #elif defined(__MACOSX__) /* this is deprecated in 10.12 sdk; favor gcc atomics. */
139  return (SDL_bool) OSAtomicCompareAndSwap32Barrier(oldval, newval, &a->value);
140 #elif defined(__SOLARIS__) && defined(_LP64)
141  return (SDL_bool) ((int) atomic_cas_64((volatile uint64_t*)&a->value, (uint64_t)oldval, (uint64_t)newval) == oldval);
142 #elif defined(__SOLARIS__) && !defined(_LP64)
143  return (SDL_bool) ((int) atomic_cas_32((volatile uint32_t*)&a->value, (uint32_t)oldval, (uint32_t)newval) == oldval);
144 #elif EMULATE_CAS
146 
147  enterLock(a);
148  if (a->value == oldval) {
149  a->value = newval;
150  retval = SDL_TRUE;
151  }
152  leaveLock(a);
153 
154  return retval;
155 #else
156  #error Please define your platform.
157 #endif
158 }

References enterLock(), leaveLock(), retval, SDL_FALSE, and SDL_TRUE.

Referenced by SDL_AtomicAdd(), SDL_AtomicGet(), and SDL_AtomicSet().

◆ SDL_AtomicCASPtr()

SDL_bool SDL_AtomicCASPtr ( void **  a,
void oldval,
void newval 
)

Set a pointer to a new value if it is currently an old value.

Returns
SDL_TRUE if the pointer was set, SDL_FALSE otherwise.
Note
If you don't know what this function is for, you shouldn't use it!

Definition at line 161 of file SDL_atomic.c.

162 {
163 #if defined(HAVE_MSC_ATOMICS) && (_M_IX86)
164  return (_InterlockedCompareExchange((long*)a, (long)newval, (long)oldval) == (long)oldval);
165 #elif defined(HAVE_MSC_ATOMICS) && (!_M_IX86)
166  return (_InterlockedCompareExchangePointer(a, newval, oldval) == oldval);
167 #elif defined(HAVE_WATCOM_ATOMICS)
168  return (SDL_bool) _SDL_cmpxchg_watcom((int *)a, (long)newval, (long)oldval);
169 #elif defined(HAVE_GCC_ATOMICS)
170  return __sync_bool_compare_and_swap(a, oldval, newval);
171 #elif defined(__MACOSX__) && defined(__LP64__) /* this is deprecated in 10.12 sdk; favor gcc atomics. */
172  return (SDL_bool) OSAtomicCompareAndSwap64Barrier((int64_t)oldval, (int64_t)newval, (int64_t*) a);
173 #elif defined(__MACOSX__) && !defined(__LP64__) /* this is deprecated in 10.12 sdk; favor gcc atomics. */
174  return (SDL_bool) OSAtomicCompareAndSwap32Barrier((int32_t)oldval, (int32_t)newval, (int32_t*) a);
175 #elif defined(__SOLARIS__)
176  return (SDL_bool) (atomic_cas_ptr(a, oldval, newval) == oldval);
177 #elif EMULATE_CAS
179 
180  enterLock(a);
181  if (*a == oldval) {
182  *a = newval;
183  retval = SDL_TRUE;
184  }
185  leaveLock(a);
186 
187  return retval;
188 #else
189  #error Please define your platform.
190 #endif
191 }

References enterLock(), leaveLock(), retval, SDL_FALSE, and SDL_TRUE.

Referenced by SDL_AtomicGetPtr(), and SDL_AtomicSetPtr().

◆ SDL_AtomicGet()

int SDL_AtomicGet ( SDL_atomic_t a)

Get the value of an atomic variable.

Definition at line 265 of file SDL_atomic.c.

266 {
267 #ifdef HAVE_ATOMIC_LOAD_N
268  return __atomic_load_n(&a->value, __ATOMIC_SEQ_CST);
269 #else
270  int value;
271  do {
272  value = a->value;
273  } while (!SDL_AtomicCAS(a, value, value));
274  return value;
275 #endif
276 }

References SDL_AtomicCAS().

◆ SDL_AtomicGetPtr()

void* SDL_AtomicGetPtr ( void **  a)

Get the value of a pointer atomically.

Definition at line 279 of file SDL_atomic.c.

280 {
281 #ifdef HAVE_ATOMIC_LOAD_N
282  return __atomic_load_n(a, __ATOMIC_SEQ_CST);
283 #else
284  void *value;
285  do {
286  value = *a;
287  } while (!SDL_AtomicCASPtr(a, value, value));
288  return value;
289 #endif
290 }

References SDL_AtomicCASPtr().

◆ SDL_AtomicLock()

void SDL_AtomicLock ( SDL_SpinLock lock)

Lock a spin lock by setting it to a non-zero value.

Parameters
lockPoints to the lock.

Definition at line 152 of file SDL_spinlock.c.

153 {
154  int iterations = 0;
155  /* FIXME: Should we have an eventual timeout? */
156  while (!SDL_AtomicTryLock(lock)) {
157  if (iterations < 32) {
158  iterations++;
160  } else {
161  /* !!! FIXME: this doesn't definitely give up the current timeslice, it does different things on various platforms. */
162  SDL_Delay(0);
163  }
164  }
165 }

References iterations, lock, PAUSE_INSTRUCTION, SDL_AtomicTryLock(), and SDL_Delay.

◆ SDL_AtomicSet()

int SDL_AtomicSet ( SDL_atomic_t a,
int  v 
)

Set an atomic variable to a value.

Returns
The previous value of the atomic variable.

Definition at line 194 of file SDL_atomic.c.

195 {
196 #ifdef HAVE_MSC_ATOMICS
197  return _InterlockedExchange((long*)&a->value, v);
198 #elif defined(HAVE_WATCOM_ATOMICS)
199  return _SDL_xchg_watcom(&a->value, v);
200 #elif defined(HAVE_GCC_ATOMICS)
201  return __sync_lock_test_and_set(&a->value, v);
202 #elif defined(__SOLARIS__) && defined(_LP64)
203  return (int) atomic_swap_64((volatile uint64_t*)&a->value, (uint64_t)v);
204 #elif defined(__SOLARIS__) && !defined(_LP64)
205  return (int) atomic_swap_32((volatile uint32_t*)&a->value, (uint32_t)v);
206 #else
207  int value;
208  do {
209  value = a->value;
210  } while (!SDL_AtomicCAS(a, value, v));
211  return value;
212 #endif
213 }

References SDL_AtomicCAS().

◆ SDL_AtomicSetPtr()

void* SDL_AtomicSetPtr ( void **  a,
void v 
)

Set a pointer to a value atomically.

Returns
The previous value of the pointer.

Definition at line 216 of file SDL_atomic.c.

217 {
218 #if defined(HAVE_MSC_ATOMICS) && (_M_IX86)
219  return (void *) _InterlockedExchange((long *)a, (long) v);
220 #elif defined(HAVE_MSC_ATOMICS) && (!_M_IX86)
221  return _InterlockedExchangePointer(a, v);
222 #elif defined(HAVE_WATCOM_ATOMICS)
223  return (void *) _SDL_xchg_watcom((int *)a, (long)v);
224 #elif defined(HAVE_GCC_ATOMICS)
225  return __sync_lock_test_and_set(a, v);
226 #elif defined(__SOLARIS__)
227  return atomic_swap_ptr(a, v);
228 #else
229  void *value;
230  do {
231  value = *a;
232  } while (!SDL_AtomicCASPtr(a, value, v));
233  return value;
234 #endif
235 }

References SDL_AtomicCASPtr().

◆ SDL_AtomicTryLock()

SDL_bool SDL_AtomicTryLock ( SDL_SpinLock lock)

Try to lock a spin lock by setting it to a non-zero value.

Parameters
lockPoints to the lock.
Returns
SDL_TRUE if the lock succeeded, SDL_FALSE if the lock is already held.

Definition at line 55 of file SDL_spinlock.c.

56 {
57 #if SDL_ATOMIC_DISABLED
58  /* Terrible terrible damage */
59  static SDL_mutex *_spinlock_mutex;
60 
61  if (!_spinlock_mutex) {
62  /* Race condition on first lock... */
63  _spinlock_mutex = SDL_CreateMutex();
64  }
65  SDL_LockMutex(_spinlock_mutex);
66  if (*lock == 0) {
67  *lock = 1;
68  SDL_UnlockMutex(_spinlock_mutex);
69  return SDL_TRUE;
70  } else {
71  SDL_UnlockMutex(_spinlock_mutex);
72  return SDL_FALSE;
73  }
74 
75 #elif defined(_MSC_VER)
76  SDL_COMPILE_TIME_ASSERT(locksize, sizeof(*lock) == sizeof(long));
77  return (InterlockedExchange((long*)lock, 1) == 0);
78 
79 #elif defined(__WATCOMC__) && defined(__386__)
80  return _SDL_xchg_watcom(lock, 1) == 0;
81 
82 #elif HAVE_GCC_ATOMICS || HAVE_GCC_SYNC_LOCK_TEST_AND_SET
83  return (__sync_lock_test_and_set(lock, 1) == 0);
84 
85 #elif defined(__GNUC__) && defined(__arm__) && \
86  (defined(__ARM_ARCH_3__) || defined(__ARM_ARCH_3M__) || \
87  defined(__ARM_ARCH_4__) || defined(__ARM_ARCH_4T__) || \
88  defined(__ARM_ARCH_5__) || defined(__ARM_ARCH_5TE__) || \
89  defined(__ARM_ARCH_5TEJ__))
90  int result;
91 
92 #if defined(__RISCOS__)
93  if (__cpucap_have_rex()) {
94  __asm__ __volatile__ (
95  "ldrex %0, [%2]\nteq %0, #0\nstrexeq %0, %1, [%2]"
96  : "=&r" (result) : "r" (1), "r" (lock) : "cc", "memory");
97  return (result == 0);
98  }
99 #endif
100 
101  __asm__ __volatile__ (
102  "swp %0, %1, [%2]\n"
103  : "=&r,&r" (result) : "r,0" (1), "r,r" (lock) : "memory");
104  return (result == 0);
105 
106 #elif defined(__GNUC__) && defined(__arm__)
107  int result;
108  __asm__ __volatile__ (
109  "ldrex %0, [%2]\nteq %0, #0\nstrexeq %0, %1, [%2]"
110  : "=&r" (result) : "r" (1), "r" (lock) : "cc", "memory");
111  return (result == 0);
112 
113 #elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
114  int result;
115  __asm__ __volatile__(
116  "lock ; xchgl %0, (%1)\n"
117  : "=r" (result) : "r" (lock), "0" (1) : "cc", "memory");
118  return (result == 0);
119 
120 #elif defined(__MACOSX__) || defined(__IPHONEOS__)
121  /* Maybe used for PowerPC, but the Intel asm or gcc atomics are favored. */
122  return OSAtomicCompareAndSwap32Barrier(0, 1, lock);
123 
124 #elif defined(__SOLARIS__) && defined(_LP64)
125  /* Used for Solaris with non-gcc compilers. */
126  return (SDL_bool) ((int) atomic_cas_64((volatile uint64_t*)lock, 0, 1) == 0);
127 
128 #elif defined(__SOLARIS__) && !defined(_LP64)
129  /* Used for Solaris with non-gcc compilers. */
130  return (SDL_bool) ((int) atomic_cas_32((volatile uint32_t*)lock, 0, 1) == 0);
131 
132 #else
133 #error Please implement for your platform.
134  return SDL_FALSE;
135 #endif
136 }

References lock, SDL_COMPILE_TIME_ASSERT, SDL_CreateMutex, SDL_FALSE, SDL_LockMutex, SDL_TRUE, and SDL_UnlockMutex.

Referenced by SDL_AtomicLock().

◆ SDL_AtomicUnlock()

void SDL_AtomicUnlock ( SDL_SpinLock lock)

Unlock a spin lock by setting it to 0. Always returns immediately.

Parameters
lockPoints to the lock.

Definition at line 168 of file SDL_spinlock.c.

169 {
170 #if defined(_MSC_VER)
171  _ReadWriteBarrier();
172  *lock = 0;
173 
174 #elif defined(__WATCOMC__) && defined(__386__)
176  *lock = 0;
177 
178 #elif HAVE_GCC_ATOMICS || HAVE_GCC_SYNC_LOCK_TEST_AND_SET
179  __sync_lock_release(lock);
180 
181 #elif defined(__SOLARIS__)
182  /* Used for Solaris when not using gcc. */
183  *lock = 0;
184  membar_producer();
185 
186 #else
187  *lock = 0;
188 #endif
189 }

References lock, and SDL_CompilerBarrier.

◆ SDL_MemoryBarrierAcquireFunction()

void SDL_MemoryBarrierAcquireFunction ( void  )

Definition at line 303 of file SDL_atomic.c.

304 {
306 }

References SDL_MemoryBarrierAcquire.

◆ SDL_MemoryBarrierReleaseFunction()

void SDL_MemoryBarrierReleaseFunction ( void  )

Memory barriers are designed to prevent reads and writes from being reordered by the compiler and being seen out of order on multi-core CPUs.

A typical pattern would be for thread A to write some data and a flag, and for thread B to read the flag and get the data. In this case you would insert a release barrier between writing the data and the flag, guaranteeing that the data write completes no later than the flag is written, and you would insert an acquire barrier between reading the flag and reading the data, to ensure that all the reads associated with the flag have completed.

In this pattern you should always see a release barrier paired with an acquire barrier and you should gate the data reads/writes with a single flag variable.

For more information on these semantics, take a look at the blog post: http://preshing.com/20120913/acquire-and-release-semantics

Definition at line 658 of file SDL_dynapi_procs.h.

References SDL_MemoryBarrierRelease.

SDL_LockMutex
#define SDL_LockMutex
Definition: SDL_dynapi_overrides.h:260
leaveLock
static SDL_INLINE void leaveLock(void *a)
Definition: SDL_atomic.c:120
SDL_mutex
Definition: SDL_sysmutex.c:30
iterations
static int iterations
Definition: testsprite2.c:45
SDL_CreateMutex
#define SDL_CreateMutex
Definition: SDL_dynapi_overrides.h:259
SDL_AtomicTryLock
SDL_bool SDL_AtomicTryLock(SDL_SpinLock *lock)
Try to lock a spin lock by setting it to a non-zero value.
Definition: SDL_spinlock.c:55
a
GLboolean GLboolean GLboolean GLboolean a
Definition: SDL_opengl_glext.h:1112
v
const GLdouble * v
Definition: SDL_opengl.h:2064
result
GLuint64EXT * result
Definition: SDL_opengl_glext.h:9435
SDL_COMPILE_TIME_ASSERT
#define SDL_COMPILE_TIME_ASSERT(name, x)
Definition: SDL_stdinc.h:312
retval
SDL_bool retval
Definition: testgamecontroller.c:65
int32_t
signed int int32_t
Definition: SDL_config_windows.h:62
SDL_AtomicCASPtr
SDL_bool SDL_AtomicCASPtr(void **a, void *oldval, void *newval)
Set a pointer to a new value if it is currently an old value.
Definition: SDL_atomic.c:161
enterLock
static SDL_INLINE void enterLock(void *a)
Definition: SDL_atomic.c:112
SDL_CompilerBarrier
#define SDL_CompilerBarrier()
Definition: SDL_atomic.h:132
SDL_TRUE
@ SDL_TRUE
Definition: SDL_stdinc.h:164
SDL_Delay
#define SDL_Delay
Definition: SDL_dynapi_overrides.h:486
uint32_t
unsigned int uint32_t
Definition: SDL_config_windows.h:63
value
GLsizei const GLfloat * value
Definition: SDL_opengl_glext.h:701
SDL_MemoryBarrierAcquire
#define SDL_MemoryBarrierAcquire()
Definition: SDL_atomic.h:208
PAUSE_INSTRUCTION
#define PAUSE_INSTRUCTION()
Definition: SDL_spinlock.c:148
SDL_bool
SDL_bool
Definition: SDL_stdinc.h:162
SDL_FALSE
@ SDL_FALSE
Definition: SDL_stdinc.h:163
SDL_UnlockMutex
#define SDL_UnlockMutex
Definition: SDL_dynapi_overrides.h:262
SDL_AtomicCAS
SDL_bool SDL_AtomicCAS(SDL_atomic_t *a, int oldval, int newval)
Set an atomic variable to a new value if it is currently an old value.
Definition: SDL_atomic.c:130
uint64_t
unsigned long long uint64_t
Definition: SDL_config_windows.h:65
int64_t
signed long long int64_t
Definition: SDL_config_windows.h:64
lock
SDL_mutex * lock
Definition: SDL_events.c:83