include/SDL_atomic.h
author Sam Lantinga <slouken@libsdl.org>
Thu, 07 Mar 2013 20:42:55 -0800
changeset 6978 e851407e9294
parent 6923 d3b709232670
child 7004 18d4df6942c2
permissions -rw-r--r--
Put the real SDL_AtomicCAS() and SDL_AtomicCASPtr() symbols into the library.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2013 Sam Lantinga <slouken@libsdl.org>
     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 
    22 /**
    23  * \file SDL_atomic.h
    24  * 
    25  * Atomic operations.
    26  * 
    27  * IMPORTANT:
    28  * If you are not an expert in concurrent lockless programming, you should
    29  * only be using the atomic lock and reference counting functions in this
    30  * file.  In all other cases you should be protecting your data structures
    31  * with full mutexes.
    32  * 
    33  * The list of "safe" functions to use are:
    34  *  SDL_AtomicLock()
    35  *  SDL_AtomicUnlock()
    36  *  SDL_AtomicIncRef()
    37  *  SDL_AtomicDecRef()
    38  * 
    39  * Seriously, here be dragons!
    40  * ^^^^^^^^^^^^^^^^^^^^^^^^^^^
    41  *
    42  * You can find out a little more about lockless programming and the 
    43  * subtle issues that can arise here:
    44  * http://msdn.microsoft.com/en-us/library/ee418650%28v=vs.85%29.aspx
    45  *
    46  * There's also lots of good information here:
    47  * http://www.1024cores.net/home/lock-free-algorithms
    48  *
    49  * These operations may or may not actually be implemented using
    50  * processor specific atomic operations. When possible they are
    51  * implemented as true processor specific atomic operations. When that
    52  * is not possible the are implemented using locks that *do* use the
    53  * available atomic operations.
    54  *
    55  * All of the atomic operations that modify memory are full memory barriers.
    56  */
    57 
    58 #ifndef _SDL_atomic_h_
    59 #define _SDL_atomic_h_
    60 
    61 #include "SDL_stdinc.h"
    62 #include "SDL_platform.h"
    63 
    64 #include "begin_code.h"
    65 
    66 /* Need to do this here because intrin.h has C++ code in it */
    67 /* Visual Studio 2005 has a bug where intrin.h conflicts with winnt.h */
    68 #if defined(_MSC_VER) && (_MSC_VER >= 1500)
    69 #include <intrin.h>
    70 #define HAVE_MSC_ATOMICS 1
    71 #endif
    72 
    73 /* Set up for C function definitions, even when using C++ */
    74 #ifdef __cplusplus
    75 /* *INDENT-OFF* */
    76 extern "C" {
    77 /* *INDENT-ON* */
    78 #endif
    79 
    80 /**
    81  * \name SDL AtomicLock
    82  * 
    83  * The atomic locks are efficient spinlocks using CPU instructions,
    84  * but are vulnerable to starvation and can spin forever if a thread
    85  * holding a lock has been terminated.  For this reason you should
    86  * minimize the code executed inside an atomic lock and never do
    87  * expensive things like API or system calls while holding them.
    88  *
    89  * The atomic locks are not safe to lock recursively.
    90  *
    91  * Porting Note:
    92  * The spin lock functions and type are required and can not be
    93  * emulated because they are used in the atomic emulation code.
    94  */
    95 /*@{*/
    96 
    97 typedef int SDL_SpinLock;
    98 
    99 /**
   100  * \brief Try to lock a spin lock by setting it to a non-zero value.
   101  * 
   102  * \param lock Points to the lock.
   103  *
   104  * \return SDL_TRUE if the lock succeeded, SDL_FALSE if the lock is already held.
   105  */
   106 extern DECLSPEC SDL_bool SDLCALL SDL_AtomicTryLock(SDL_SpinLock *lock);
   107 
   108 /**
   109  * \brief Lock a spin lock by setting it to a non-zero value.
   110  * 
   111  * \param lock Points to the lock.
   112  */
   113 extern DECLSPEC void SDLCALL SDL_AtomicLock(SDL_SpinLock *lock);
   114 
   115 /**
   116  * \brief Unlock a spin lock by setting it to 0. Always returns immediately
   117  *
   118  * \param lock Points to the lock.
   119  */
   120 extern DECLSPEC void SDLCALL SDL_AtomicUnlock(SDL_SpinLock *lock);
   121 
   122 /*@}*//*SDL AtomicLock*/
   123 
   124 
   125 /**
   126  * The compiler barrier prevents the compiler from reordering
   127  * reads and writes to globally visible variables across the call.
   128  */
   129 #if defined(_MSC_VER) && (_MSC_VER > 1200)
   130 void _ReadWriteBarrier(void);
   131 #pragma intrinsic(_ReadWriteBarrier)
   132 #define SDL_CompilerBarrier()   _ReadWriteBarrier()
   133 #elif defined(__GNUC__)
   134 #define SDL_CompilerBarrier()   __asm__ __volatile__ ("" : : : "memory")
   135 #else
   136 #define SDL_CompilerBarrier()   \
   137 { SDL_SpinLock _tmp = 0; SDL_AtomicLock(&_tmp); SDL_AtomicUnlock(&_tmp); }
   138 #endif
   139 
   140 /* Platform specific optimized versions of the atomic functions,
   141  * you can disable these by defining SDL_DISABLE_ATOMIC_INLINE
   142  */
   143 #if defined(SDL_ATOMIC_DISABLED) && SDL_ATOMIC_DISABLED
   144 #define SDL_DISABLE_ATOMIC_INLINE
   145 #endif
   146 #ifndef SDL_DISABLE_ATOMIC_INLINE
   147 
   148 #ifdef HAVE_MSC_ATOMICS
   149 
   150 #define SDL_AtomicSet(a, v)     _InterlockedExchange((long*)&(a)->value, (v))
   151 #define SDL_AtomicAdd(a, v)     _InterlockedExchangeAdd((long*)&(a)->value, (v))
   152 #define SDL_AtomicCAS(a, oldval, newval) (_InterlockedCompareExchange((long*)&(a)->value, (newval), (oldval)) == (oldval))
   153 #define SDL_AtomicSetPtr(a, v)  _InterlockedExchangePointer((a), (v))
   154 #if _M_IX86
   155 #define SDL_AtomicCASPtr(a, oldval, newval) (_InterlockedCompareExchange((long*)(a), (long)(newval), (long)(oldval)) == (long)(oldval))
   156 #else
   157 #define SDL_AtomicCASPtr(a, oldval, newval) (_InterlockedCompareExchangePointer((a), (newval), (oldval)) == (oldval))
   158 #endif
   159 
   160 #elif defined(__MACOSX__)
   161 #include <libkern/OSAtomic.h>
   162 
   163 #define SDL_AtomicCAS(a, oldval, newval) OSAtomicCompareAndSwap32Barrier((oldval), (newval), &(a)->value)
   164 #ifdef __LP64__
   165 #define SDL_AtomicCASPtr(a, oldval, newval) OSAtomicCompareAndSwap64Barrier((int64_t)(oldval), (int64_t)(newval), (int64_t*)(a))
   166 #else
   167 #define SDL_AtomicCASPtr(a, oldval, newval) OSAtomicCompareAndSwap32Barrier((int32_t)(oldval), (int32_t)(newval), (int32_t*)(a))
   168 #endif
   169 
   170 #elif defined(HAVE_GCC_ATOMICS)
   171 
   172 #define SDL_AtomicSet(a, v)     __sync_lock_test_and_set(&(a)->value, v)
   173 #define SDL_AtomicAdd(a, v)     __sync_fetch_and_add(&(a)->value, v)
   174 #define SDL_AtomicSetPtr(a, v)  __sync_lock_test_and_set(a, v)
   175 #define SDL_AtomicCAS(a, oldval, newval) __sync_bool_compare_and_swap(&(a)->value, oldval, newval)
   176 #define SDL_AtomicCASPtr(a, oldval, newval) __sync_bool_compare_and_swap(a, oldval, newval)
   177 
   178 #endif
   179 
   180 #endif /* !SDL_DISABLE_ATOMIC_INLINE */
   181 
   182 
   183 /**
   184  * \brief A type representing an atomic integer value.  It is a struct
   185  *        so people don't accidentally use numeric operations on it.
   186  */
   187 #ifndef SDL_atomic_t_defined
   188 typedef struct { int value; } SDL_atomic_t;
   189 #endif
   190 
   191 /**
   192  * \brief Set an atomic variable to a new value if it is currently an old value.
   193  *
   194  * \return SDL_TRUE if the atomic variable was set, SDL_FALSE otherwise.
   195  *
   196  * \note If you don't know what this function is for, you shouldn't use it!
   197 */
   198 #ifndef SDL_AtomicCAS
   199 extern DECLSPEC SDL_bool SDLCALL SDL_AtomicCAS(SDL_atomic_t *a, int oldval, int newval);
   200 #endif
   201 
   202 /**
   203  * \brief Set an atomic variable to a value.
   204  *
   205  * \return The previous value of the atomic variable.
   206  */
   207 #ifndef SDL_AtomicSet
   208 static __inline__ int SDL_AtomicSet(SDL_atomic_t *a, int v)
   209 {
   210     int value;
   211     do {
   212         value = a->value;
   213     } while (!SDL_AtomicCAS(a, value, v));
   214     return value;
   215 }
   216 #endif
   217 
   218 /**
   219  * \brief Get the value of an atomic variable
   220  */
   221 #ifndef SDL_AtomicGet
   222 static __inline__ int SDL_AtomicGet(SDL_atomic_t *a)
   223 {
   224     int value = a->value;
   225     SDL_CompilerBarrier();
   226     return value;
   227 }
   228 #endif
   229 
   230 /**
   231  * \brief Add to an atomic variable.
   232  *
   233  * \return The previous value of the atomic variable.
   234  *
   235  * \note This same style can be used for any number operation
   236  */
   237 #ifndef SDL_AtomicAdd
   238 static __inline__ int SDL_AtomicAdd(SDL_atomic_t *a, int v)
   239 {
   240     int value;
   241     do {
   242         value = a->value;
   243     } while (!SDL_AtomicCAS(a, value, (value + v)));
   244     return value;
   245 }
   246 #endif
   247 
   248 /**
   249  * \brief Increment an atomic variable used as a reference count.
   250  */
   251 #ifndef SDL_AtomicIncRef
   252 #define SDL_AtomicIncRef(a)    SDL_AtomicAdd(a, 1)
   253 #endif
   254 
   255 /**
   256  * \brief Decrement an atomic variable used as a reference count.
   257  *
   258  * \return SDL_TRUE if the variable reached zero after decrementing,
   259  *         SDL_FALSE otherwise
   260  */
   261 #ifndef SDL_AtomicDecRef
   262 #define SDL_AtomicDecRef(a)    (SDL_AtomicAdd(a, -1) == 1)
   263 #endif
   264 
   265 /**
   266  * \brief Set a pointer to a new value if it is currently an old value.
   267  *
   268  * \return SDL_TRUE if the pointer was set, SDL_FALSE otherwise.
   269  *
   270  * \note If you don't know what this function is for, you shouldn't use it!
   271 */
   272 #ifndef SDL_AtomicCASPtr
   273 extern DECLSPEC SDL_bool SDLCALL SDL_AtomicCASPtr(void* *a, void *oldval, void *newval);
   274 #endif
   275 
   276 /**
   277  * \brief Set a pointer to a value atomically.
   278  *
   279  * \return The previous value of the pointer.
   280  */
   281 #ifndef SDL_AtomicSetPtr
   282 static __inline__ void* SDL_AtomicSetPtr(void* *a, void* v)
   283 {
   284     void* value;
   285     do {
   286         value = *a;
   287     } while (!SDL_AtomicCASPtr(a, value, v));
   288     return value;
   289 }
   290 #endif
   291 
   292 /**
   293  * \brief Get the value of a pointer atomically.
   294  */
   295 #ifndef SDL_AtomicGetPtr
   296 static __inline__ void* SDL_AtomicGetPtr(void* *a)
   297 {
   298     void* value = *a;
   299     SDL_CompilerBarrier();
   300     return value;
   301 }
   302 #endif
   303 
   304 
   305 /* Ends C function definitions when using C++ */
   306 #ifdef __cplusplus
   307 /* *INDENT-OFF* */
   308 }
   309 /* *INDENT-ON* */
   310 #endif
   311 
   312 #include "close_code.h"
   313 
   314 #endif /* _SDL_atomic_h_ */
   315 
   316 /* vi: set ts=4 sw=4 expandtab: */