include/SDL_atomic.h
changeset 5095 dceec93471e7
parent 5086 c2539ff054c8
child 5096 e4301cde4de1
     1.1 --- a/include/SDL_atomic.h	Mon Jan 24 23:54:21 2011 -0600
     1.2 +++ b/include/SDL_atomic.h	Tue Jan 25 17:40:06 2011 -0800
     1.3 @@ -38,8 +38,13 @@
     1.4   *  SDL_AtomicDecRef()
     1.5   * 
     1.6   * Seriously, here be dragons!
     1.7 + * ^^^^^^^^^^^^^^^^^^^^^^^^^^^
     1.8   *
     1.9 - * These operations may, or may not, actually be implemented using
    1.10 + * You can find out a little more about lockless programming and the 
    1.11 + * subtle issues that can arise here:
    1.12 + * http://msdn.microsoft.com/en-us/library/ee418650%28v=vs.85%29.aspx
    1.13 + *
    1.14 + * These operations may or may not actually be implemented using
    1.15   * processor specific atomic operations. When possible they are
    1.16   * implemented as true processor specific atomic operations. When that
    1.17   * is not possible the are implemented using locks that *do* use the
    1.18 @@ -114,66 +119,54 @@
    1.19  
    1.20  /*@}*//*SDL AtomicLock*/
    1.21  
    1.22 +
    1.23 +/* The compiler barrier prevents the compiler from reordering
    1.24 +   reads and writes to globally visible variables across the call.
    1.25 +*/
    1.26 +#ifdef _MSC_VER
    1.27 +void _ReadWriteBarrier(void);
    1.28 +#pragma intrinsic(_ReadWriteBarrier)
    1.29 +#define SDL_CompilerBarrier()   _ReadWriteBarrier()
    1.30 +#elif __GNUC__
    1.31 +#define SDL_CompilerBarrier()   __asm__ __volatile__ ("" : : : "memory")
    1.32 +#else
    1.33 +#define SDL_CompilerBarrier()   \
    1.34 +({ SDL_SpinLock _tmp = 0; SDL_AtomicLock(&_tmp); SDL_AtomicUnlock(&_tmp); })
    1.35 +#endif
    1.36 +
    1.37  /* Platform specific optimized versions of the atomic functions,
    1.38   * you can disable these by defining SDL_DISABLE_ATOMIC_INLINE
    1.39   */
    1.40  #ifndef SDL_DISABLE_ATOMIC_INLINE
    1.41  
    1.42 -#if defined(HAVE_MSC_ATOMICS)
    1.43 +#if HAVE_MSC_ATOMICS
    1.44  
    1.45  #define SDL_AtomicSet(a, v)     _InterlockedExchange((long*)&(a)->value, (v))
    1.46 -#define SDL_AtomicGet(a)        ((a)->value)
    1.47  #define SDL_AtomicAdd(a, v)     _InterlockedExchangeAdd((long*)&(a)->value, (v))
    1.48  #define SDL_AtomicCAS(a, oldval, newval) (_InterlockedCompareExchange((long*)&(a)->value, (newval), (oldval)) == (oldval))
    1.49 -#define SDL_AtomicSetPtr(a, v)  (void)_InterlockedExchangePointer((a), (v))
    1.50 -#define SDL_AtomicGetPtr(a)     (*(a))
    1.51 +#define SDL_AtomicSetPtr(a, v)  _InterlockedExchangePointer((a), (v))
    1.52  #if _M_IX86
    1.53  #define SDL_AtomicCASPtr(a, oldval, newval) (_InterlockedCompareExchange((long*)(a), (long)(newval), (long)(oldval)) == (long)(oldval))
    1.54  #else
    1.55  #define SDL_AtomicCASPtr(a, oldval, newval) (_InterlockedCompareExchangePointer((a), (newval), (oldval)) == (oldval))
    1.56  #endif
    1.57  
    1.58 -#elif defined(__MACOSX__)
    1.59 +#elif __MACOSX__
    1.60  #include <libkern/OSAtomic.h>
    1.61  
    1.62 -#define SDL_AtomicSet(a, v) \
    1.63 -({                          \
    1.64 -    int oldvalue;           \
    1.65 -                            \
    1.66 -    do {                    \
    1.67 -        oldvalue = (a)->value; \
    1.68 -    } while (!OSAtomicCompareAndSwap32Barrier(oldvalue, v, &(a)->value)); \
    1.69 -                            \
    1.70 -    oldvalue;               \
    1.71 -})
    1.72 -#define SDL_AtomicGet(a)        ((a)->value)
    1.73 -#define SDL_AtomicAdd(a, v) \
    1.74 -({                          \
    1.75 -    int oldvalue;           \
    1.76 -                            \
    1.77 -    do {                    \
    1.78 -        oldvalue = (a)->value; \
    1.79 -    } while (!OSAtomicCompareAndSwap32Barrier(oldvalue, oldvalue+v, &(a)->value)); \
    1.80 -                            \
    1.81 -    oldvalue;               \
    1.82 -})
    1.83 -#define SDL_AtomicCAS(a, oldval, newval) OSAtomicCompareAndSwap32Barrier(oldval, newval, &(a)->value)
    1.84 -#define SDL_AtomicSetPtr(a, v)  (*(a) = v, OSMemoryBarrier())
    1.85 -#define SDL_AtomicGetPtr(a)     (*(a))
    1.86 +#define SDL_AtomicCAS(a, oldval, newval) OSAtomicCompareAndSwap32Barrier((oldval), (newval), &(a)->value)
    1.87  #if SIZEOF_VOIDP == 4
    1.88  #define SDL_AtomicCASPtr(a, oldval, newval) OSAtomicCompareAndSwap32Barrier((int32_t)(oldval), (int32_t)(newval), (int32_t*)(a))
    1.89  #elif SIZEOF_VOIDP == 8
    1.90  #define SDL_AtomicCASPtr(a, oldval, newval) OSAtomicCompareAndSwap64Barrier((int64_t)(oldval), (int64_t)(newval), (int64_t*)(a))
    1.91  #endif
    1.92  
    1.93 -#elif defined(HAVE_GCC_ATOMICS)
    1.94 +#elif HAVE_GCC_ATOMICS
    1.95  
    1.96  #define SDL_AtomicSet(a, v)     __sync_lock_test_and_set(&(a)->value, v)
    1.97 -#define SDL_AtomicGet(a)        ((a)->value)
    1.98  #define SDL_AtomicAdd(a, v)     __sync_fetch_and_add(&(a)->value, v)
    1.99 +#define SDL_AtomicSetPtr(a, v)  __sync_lock_test_and_set(a, v)
   1.100  #define SDL_AtomicCAS(a, oldval, newval) __sync_bool_compare_and_swap(&(a)->value, oldval, newval)
   1.101 -#define SDL_AtomicSetPtr(a, v)  (*(a) = v, __sync_synchronize())
   1.102 -#define SDL_AtomicGetPtr(a)     (*(a))
   1.103  #define SDL_AtomicCASPtr(a, oldval, newval) __sync_bool_compare_and_swap(a, oldval, newval)
   1.104  
   1.105  #endif
   1.106 @@ -195,40 +188,61 @@
   1.107   * \return The previous value of the atomic variable.
   1.108   */
   1.109  #ifndef SDL_AtomicSet
   1.110 -extern DECLSPEC int SDLCALL SDL_AtomicSet(SDL_atomic_t *a, int value);
   1.111 +#define SDL_AtomicSet(a, v) \
   1.112 +({                              \
   1.113 +    int _value;                 \
   1.114 +    do {                        \
   1.115 +        _value = (a)->value;    \
   1.116 +    } while (!SDL_AtomicCAS(a, _value, (v))); \
   1.117 +    _value;                     \
   1.118 +})
   1.119  #endif
   1.120  
   1.121  /**
   1.122   * \brief Get the value of an atomic variable
   1.123   */
   1.124  #ifndef SDL_AtomicGet
   1.125 -extern DECLSPEC int SDLCALL SDL_AtomicGet(SDL_atomic_t *a);
   1.126 +#define SDL_AtomicGet(a)        \
   1.127 +({                              \
   1.128 +    int _value = (a)->value;    \
   1.129 +    SDL_CompilerBarrier();      \
   1.130 +    _value;                     \
   1.131 +})
   1.132  #endif
   1.133  
   1.134  /**
   1.135 - * \brief  Add to an atomic variable.
   1.136 + * \brief Add to an atomic variable.
   1.137   *
   1.138   * \return The previous value of the atomic variable.
   1.139 + *
   1.140 + * \note This same style can be used for any number operation
   1.141   */
   1.142  #ifndef SDL_AtomicAdd
   1.143 -extern DECLSPEC int SDLCALL SDL_AtomicAdd(SDL_atomic_t *a, int value);
   1.144 +#define SDL_AtomicAdd(a, v)     \
   1.145 +({                              \
   1.146 +    int _value;                 \
   1.147 +    do {                        \
   1.148 +        _value = (a)->value;    \
   1.149 +    } while (!SDL_AtomicCAS(a, _value, (_value + (v)))); \
   1.150 +    _value;                     \
   1.151 +})
   1.152  #endif
   1.153  
   1.154  /**
   1.155   * \brief Increment an atomic variable used as a reference count.
   1.156   */
   1.157  #ifndef SDL_AtomicIncRef
   1.158 -extern DECLSPEC void SDLCALL SDL_AtomicIncRef(SDL_atomic_t *a);
   1.159 +#define SDL_AtomicIncRef(a)    SDL_AtomicAdd(a, 1)
   1.160  #endif
   1.161  
   1.162  /**
   1.163   * \brief Decrement an atomic variable used as a reference count.
   1.164   *
   1.165 - * \return SDL_TRUE if the variable has reached zero after decrementing,
   1.166 + * \return SDL_TRUE if the variable reached zero after decrementing,
   1.167   *         SDL_FALSE otherwise
   1.168   */
   1.169  #ifndef SDL_AtomicDecRef
   1.170 -extern DECLSPEC SDL_bool SDLCALL SDL_AtomicDecRef(SDL_atomic_t *a);
   1.171 +#define SDL_AtomicDecRef(a)    (SDL_AtomicAdd(a, -1) == 1)
   1.172  #endif
   1.173  
   1.174  /**
   1.175 @@ -239,21 +253,36 @@
   1.176   * \note If you don't know what this function is for, you shouldn't use it!
   1.177  */
   1.178  #ifndef SDL_AtomicCAS
   1.179 -extern DECLSPEC SDL_bool SDLCALL SDL_AtomicCAS(SDL_atomic_t *a, int oldval, int newval);
   1.180 +#define SDL_AtomicCAS SDL_AtomicCAS_
   1.181  #endif
   1.182 +extern DECLSPEC SDL_bool SDLCALL SDL_AtomicCAS_(SDL_atomic_t *a, int oldval, int newval);
   1.183  
   1.184  /**
   1.185   * \brief Set a pointer to a value atomically.
   1.186 + *
   1.187 + * \return The previous value of the pointer.
   1.188   */
   1.189  #ifndef SDL_AtomicSetPtr
   1.190 -extern DECLSPEC void SDLCALL SDL_AtomicSetPtr(void** a, void* value);
   1.191 +#define SDL_AtomicSetPtr(a, v)  \
   1.192 +({                              \
   1.193 +    void* _value;               \
   1.194 +    do {                        \
   1.195 +        _value = *(a);          \
   1.196 +    } while (!SDL_AtomicCASPtr(a, _value, (v))); \
   1.197 +    _value;                     \
   1.198 +})
   1.199  #endif
   1.200  
   1.201  /**
   1.202   * \brief Get the value of a pointer atomically.
   1.203   */
   1.204  #ifndef SDL_AtomicGetPtr
   1.205 -extern DECLSPEC void* SDLCALL SDL_AtomicGetPtr(void** a);
   1.206 +#define SDL_AtomicGetPtr(a)     \
   1.207 +({                              \
   1.208 +    void* _value = *(a);        \
   1.209 +    SDL_CompilerBarrier();      \
   1.210 +    _value;                     \
   1.211 +})
   1.212  #endif
   1.213  
   1.214  /**
   1.215 @@ -264,8 +293,9 @@
   1.216   * \note If you don't know what this function is for, you shouldn't use it!
   1.217  */
   1.218  #ifndef SDL_AtomicCASPtr
   1.219 -extern DECLSPEC SDL_bool SDLCALL SDL_AtomicCASPtr(void **a, void *oldval, void *newval);
   1.220 +#define SDL_AtomicCASPtr SDL_AtomicCASPtr_
   1.221  #endif
   1.222 +extern DECLSPEC SDL_bool SDLCALL SDL_AtomicCASPtr_(void **a, void *oldval, void *newval);
   1.223  
   1.224  /* Ends C function definitions when using C++ */
   1.225  #ifdef __cplusplus