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...
 

Macros

#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...
 

Functions

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...
 

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.

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...
 

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 235 of file SDL_atomic.h.

Referenced by DequeueEvent_LockFree(), EnqueueEvent_LockFree(), FIFO_Watcher(), and RunBasicTest().

◆ SDL_AtomicIncRef

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

Increment an atomic variable used as a reference count.

Definition at line 225 of file SDL_atomic.h.

Referenced by DequeueEvent_LockFree(), EnqueueEvent_LockFree(), FIFO_Watcher(), RunBasicTest(), SDL_AddTimer(), and SDL_TLSCreate().

◆ 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 129 of file SDL_atomic.h.

◆ SDL_MemoryBarrierAcquire

#define SDL_MemoryBarrierAcquire ( )    SDL_CompilerBarrier()

Definition at line 181 of file SDL_atomic.h.

Referenced by SDL_Generic_GetTLSData(), SDL_GetErrBuf(), and SDL_SYS_GetTLSData().

◆ SDL_MemoryBarrierRelease

#define SDL_MemoryBarrierRelease ( )    SDL_CompilerBarrier()

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 180 of file SDL_atomic.h.

Referenced by SDL_Generic_GetTLSData(), SDL_GetErrBuf(), and SDL_SYS_GetTLSData().

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 187 of file SDL_atomic.c.

References SDL_AtomicCAS(), and SDL_atomic_t::value.

188 {
189 #ifdef HAVE_MSC_ATOMICS
190  return _InterlockedExchangeAdd((long*)&a->value, v);
191 #elif defined(HAVE_GCC_ATOMICS)
192  return __sync_fetch_and_add(&a->value, v);
193 #elif defined(__SOLARIS__)
194  int pv = a->value;
195  membar_consumer();
196 #if defined(_LP64)
197  atomic_add_64((volatile uint64_t*)&a->value, v);
198 #elif !defined(_LP64)
199  atomic_add_32((volatile uint32_t*)&a->value, v);
200 #endif
201  return pv;
202 #else
203  int value;
204  do {
205  value = a->value;
206  } while (!SDL_AtomicCAS(a, value, (value + v)));
207  return value;
208 #endif
209 }
unsigned long long uint64_t
const GLdouble * v
Definition: SDL_opengl.h:2057
GLsizei const GLfloat * value
unsigned int uint32_t
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:87

◆ 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 87 of file SDL_atomic.c.

References enterLock(), leaveLock(), retval, SDL_FALSE, SDL_TRUE, and SDL_atomic_t::value.

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

88 {
89 #ifdef HAVE_MSC_ATOMICS
90  return (_InterlockedCompareExchange((long*)&a->value, (long)newval, (long)oldval) == (long)oldval);
91 #elif defined(__MACOSX__) /* !!! FIXME: should we favor gcc atomics? */
92  return (SDL_bool) OSAtomicCompareAndSwap32Barrier(oldval, newval, &a->value);
93 #elif defined(HAVE_GCC_ATOMICS)
94  return (SDL_bool) __sync_bool_compare_and_swap(&a->value, oldval, newval);
95 #elif defined(__SOLARIS__) && defined(_LP64)
96  return (SDL_bool) ((int) atomic_cas_64((volatile uint64_t*)&a->value, (uint64_t)oldval, (uint64_t)newval) == oldval);
97 #elif defined(__SOLARIS__) && !defined(_LP64)
98  return (SDL_bool) ((int) atomic_cas_32((volatile uint32_t*)&a->value, (uint32_t)oldval, (uint32_t)newval) == oldval);
99 #elif EMULATE_CAS
101 
102  enterLock(a);
103  if (a->value == oldval) {
104  a->value = newval;
105  retval = SDL_TRUE;
106  }
107  leaveLock(a);
108 
109  return retval;
110 #else
111  #error Please define your platform.
112 #endif
113 }
unsigned long long uint64_t
SDL_bool retval
static SDL_INLINE void leaveLock(void *a)
Definition: SDL_atomic.c:77
static SDL_INLINE void enterLock(void *a)
Definition: SDL_atomic.c:69
SDL_bool
Definition: SDL_stdinc.h:130
unsigned int uint32_t

◆ 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 116 of file SDL_atomic.c.

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

Referenced by SDL_AtomicGetPtr(), and SDL_AtomicSetPtr().

117 {
118 #if defined(HAVE_MSC_ATOMICS) && (_M_IX86)
119  return (_InterlockedCompareExchange((long*)a, (long)newval, (long)oldval) == (long)oldval);
120 #elif defined(HAVE_MSC_ATOMICS) && (!_M_IX86)
121  return (_InterlockedCompareExchangePointer(a, newval, oldval) == oldval);
122 #elif defined(__MACOSX__) && defined(__LP64__) /* !!! FIXME: should we favor gcc atomics? */
123  return (SDL_bool) OSAtomicCompareAndSwap64Barrier((int64_t)oldval, (int64_t)newval, (int64_t*) a);
124 #elif defined(__MACOSX__) && !defined(__LP64__) /* !!! FIXME: should we favor gcc atomics? */
125  return (SDL_bool) OSAtomicCompareAndSwap32Barrier((int32_t)oldval, (int32_t)newval, (int32_t*) a);
126 #elif defined(HAVE_GCC_ATOMICS)
127  return __sync_bool_compare_and_swap(a, oldval, newval);
128 #elif defined(__SOLARIS__)
129  return (SDL_bool) (atomic_cas_ptr(a, oldval, newval) == oldval);
130 #elif EMULATE_CAS
132 
133  enterLock(a);
134  if (*a == oldval) {
135  *a = newval;
136  retval = SDL_TRUE;
137  }
138  leaveLock(a);
139 
140  return retval;
141 #else
142  #error Please define your platform.
143 #endif
144 }
signed int int32_t
SDL_bool retval
static SDL_INLINE void leaveLock(void *a)
Definition: SDL_atomic.c:77
static SDL_INLINE void enterLock(void *a)
Definition: SDL_atomic.c:69
SDL_bool
Definition: SDL_stdinc.h:130
GLboolean GLboolean GLboolean GLboolean a
signed long long int64_t

◆ SDL_AtomicGet()

int SDL_AtomicGet ( SDL_atomic_t a)

Get the value of an atomic variable.

Definition at line 212 of file SDL_atomic.c.

References SDL_AtomicCAS(), and SDL_atomic_t::value.

213 {
214  int value;
215  do {
216  value = a->value;
217  } while (!SDL_AtomicCAS(a, value, value));
218  return value;
219 }
GLsizei const GLfloat * value
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:87

◆ SDL_AtomicGetPtr()

void* SDL_AtomicGetPtr ( void **  a)

Get the value of a pointer atomically.

Definition at line 222 of file SDL_atomic.c.

References SDL_AtomicCASPtr().

223 {
224  void *value;
225  do {
226  value = *a;
227  } while (!SDL_AtomicCASPtr(a, value, value));
228  return value;
229 }
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:116
GLsizei const GLfloat * value
GLboolean GLboolean GLboolean GLboolean a

◆ 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 107 of file SDL_spinlock.c.

References SDL_AtomicTryLock(), and SDL_Delay.

108 {
109  /* FIXME: Should we have an eventual timeout? */
110  while (!SDL_AtomicTryLock(lock)) {
111  SDL_Delay(0);
112  }
113 }
#define SDL_Delay
SDL_mutex * lock
Definition: SDL_events.c:75
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:37

◆ 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 147 of file SDL_atomic.c.

References SDL_AtomicCAS(), and SDL_atomic_t::value.

148 {
149 #ifdef HAVE_MSC_ATOMICS
150  return _InterlockedExchange((long*)&a->value, v);
151 #elif defined(HAVE_GCC_ATOMICS)
152  return __sync_lock_test_and_set(&a->value, v);
153 #elif defined(__SOLARIS__) && defined(_LP64)
154  return (int) atomic_swap_64((volatile uint64_t*)&a->value, (uint64_t)v);
155 #elif defined(__SOLARIS__) && !defined(_LP64)
156  return (int) atomic_swap_32((volatile uint32_t*)&a->value, (uint32_t)v);
157 #else
158  int value;
159  do {
160  value = a->value;
161  } while (!SDL_AtomicCAS(a, value, v));
162  return value;
163 #endif
164 }
unsigned long long uint64_t
const GLdouble * v
Definition: SDL_opengl.h:2057
GLsizei const GLfloat * value
unsigned int uint32_t
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:87

◆ 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 167 of file SDL_atomic.c.

References SDL_AtomicCASPtr().

168 {
169 #if defined(HAVE_MSC_ATOMICS) && (_M_IX86)
170  return (void *) _InterlockedExchange((long *)a, (long) v);
171 #elif defined(HAVE_MSC_ATOMICS) && (!_M_IX86)
172  return _InterlockedExchangePointer(a, v);
173 #elif defined(HAVE_GCC_ATOMICS)
174  return __sync_lock_test_and_set(a, v);
175 #elif defined(__SOLARIS__)
176  return atomic_swap_ptr(a, v);
177 #else
178  void *value;
179  do {
180  value = *a;
181  } while (!SDL_AtomicCASPtr(a, value, v));
182  return value;
183 #endif
184 }
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:116
const GLdouble * v
Definition: SDL_opengl.h:2057
GLsizei const GLfloat * value
GLboolean GLboolean GLboolean GLboolean a

◆ 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 37 of file SDL_spinlock.c.

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

Referenced by SDL_AtomicLock().

38 {
39 #if SDL_ATOMIC_DISABLED
40  /* Terrible terrible damage */
41  static SDL_mutex *_spinlock_mutex;
42 
43  if (!_spinlock_mutex) {
44  /* Race condition on first lock... */
45  _spinlock_mutex = SDL_CreateMutex();
46  }
47  SDL_LockMutex(_spinlock_mutex);
48  if (*lock == 0) {
49  *lock = 1;
50  SDL_UnlockMutex(_spinlock_mutex);
51  return SDL_TRUE;
52  } else {
53  SDL_UnlockMutex(_spinlock_mutex);
54  return SDL_FALSE;
55  }
56 
57 #elif defined(_MSC_VER)
58  SDL_COMPILE_TIME_ASSERT(locksize, sizeof(*lock) == sizeof(long));
59  return (InterlockedExchange((long*)lock, 1) == 0);
60 
61 #elif HAVE_GCC_ATOMICS || HAVE_GCC_SYNC_LOCK_TEST_AND_SET
62  return (__sync_lock_test_and_set(lock, 1) == 0);
63 
64 #elif defined(__GNUC__) && defined(__arm__) && \
65  (defined(__ARM_ARCH_4__) || defined(__ARM_ARCH_4T__) || \
66  defined(__ARM_ARCH_5__) || defined(__ARM_ARCH_5TE__) || \
67  defined(__ARM_ARCH_5TEJ__))
68  int result;
69  __asm__ __volatile__ (
70  "swp %0, %1, [%2]\n"
71  : "=&r,&r" (result) : "r,0" (1), "r,r" (lock) : "memory");
72  return (result == 0);
73 
74 #elif defined(__GNUC__) && defined(__arm__)
75  int result;
76  __asm__ __volatile__ (
77  "ldrex %0, [%2]\nteq %0, #0\nstrexeq %0, %1, [%2]"
78  : "=&r" (result) : "r" (1), "r" (lock) : "cc", "memory");
79  return (result == 0);
80 
81 #elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
82  int result;
83  __asm__ __volatile__(
84  "lock ; xchgl %0, (%1)\n"
85  : "=r" (result) : "r" (lock), "0" (1) : "cc", "memory");
86  return (result == 0);
87 
88 #elif defined(__MACOSX__) || defined(__IPHONEOS__)
89  /* Maybe used for PowerPC, but the Intel asm or gcc atomics are favored. */
90  return OSAtomicCompareAndSwap32Barrier(0, 1, lock);
91 
92 #elif defined(__SOLARIS__) && defined(_LP64)
93  /* Used for Solaris with non-gcc compilers. */
94  return (SDL_bool) ((int) atomic_cas_64((volatile uint64_t*)lock, 0, 1) == 0);
95 
96 #elif defined(__SOLARIS__) && !defined(_LP64)
97  /* Used for Solaris with non-gcc compilers. */
98  return (SDL_bool) ((int) atomic_cas_32((volatile uint32_t*)lock, 0, 1) == 0);
99 
100 #else
101 #error Please implement for your platform.
102  return SDL_FALSE;
103 #endif
104 }
#define SDL_LockMutex
GLuint64EXT * result
#define SDL_CreateMutex
unsigned long long uint64_t
SDL_bool
Definition: SDL_stdinc.h:130
unsigned int uint32_t
SDL_mutex * lock
Definition: SDL_events.c:75
#define SDL_UnlockMutex
#define SDL_COMPILE_TIME_ASSERT(name, x)
Definition: SDL_stdinc.h:266

◆ 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 116 of file SDL_spinlock.c.

117 {
118 #if defined(_MSC_VER)
119  _ReadWriteBarrier();
120  *lock = 0;
121 
122 #elif HAVE_GCC_ATOMICS || HAVE_GCC_SYNC_LOCK_TEST_AND_SET
123  __sync_lock_release(lock);
124 
125 #elif defined(__SOLARIS__)
126  /* Used for Solaris when not using gcc. */
127  *lock = 0;
128  membar_producer();
129 
130 #else
131  *lock = 0;
132 #endif
133 }
SDL_mutex * lock
Definition: SDL_events.c:75