include/SDL_atomic.h
author Sam Lantinga <slouken@libsdl.org>
Sat, 18 May 2013 14:17:52 -0700
changeset 7191 75360622e65f
parent 7004 18d4df6942c2
child 7393 358696c354a8
permissions -rw-r--r--
File style cleanup for the SDL 2.0 release
     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 extern "C" {
    76 #endif
    77 
    78 /**
    79  * \name SDL AtomicLock
    80  *
    81  * The atomic locks are efficient spinlocks using CPU instructions,
    82  * but are vulnerable to starvation and can spin forever if a thread
    83  * holding a lock has been terminated.  For this reason you should
    84  * minimize the code executed inside an atomic lock and never do
    85  * expensive things like API or system calls while holding them.
    86  *
    87  * The atomic locks are not safe to lock recursively.
    88  *
    89  * Porting Note:
    90  * The spin lock functions and type are required and can not be
    91  * emulated because they are used in the atomic emulation code.
    92  */
    93 /*@{*/
    94 
    95 typedef int SDL_SpinLock;
    96 
    97 /**
    98  * \brief Try to lock a spin lock by setting it to a non-zero value.
    99  *
   100  * \param lock Points to the lock.
   101  *
   102  * \return SDL_TRUE if the lock succeeded, SDL_FALSE if the lock is already held.
   103  */
   104 extern DECLSPEC SDL_bool SDLCALL SDL_AtomicTryLock(SDL_SpinLock *lock);
   105 
   106 /**
   107  * \brief Lock a spin lock by setting it to a non-zero value.
   108  *
   109  * \param lock Points to the lock.
   110  */
   111 extern DECLSPEC void SDLCALL SDL_AtomicLock(SDL_SpinLock *lock);
   112 
   113 /**
   114  * \brief Unlock a spin lock by setting it to 0. Always returns immediately
   115  *
   116  * \param lock Points to the lock.
   117  */
   118 extern DECLSPEC void SDLCALL SDL_AtomicUnlock(SDL_SpinLock *lock);
   119 
   120 /*@}*//*SDL AtomicLock*/
   121 
   122 
   123 /**
   124  * The compiler barrier prevents the compiler from reordering
   125  * reads and writes to globally visible variables across the call.
   126  */
   127 #if defined(_MSC_VER) && (_MSC_VER > 1200)
   128 void _ReadWriteBarrier(void);
   129 #pragma intrinsic(_ReadWriteBarrier)
   130 #define SDL_CompilerBarrier()   _ReadWriteBarrier()
   131 #elif defined(__GNUC__)
   132 #define SDL_CompilerBarrier()   __asm__ __volatile__ ("" : : : "memory")
   133 #else
   134 #define SDL_CompilerBarrier()   \
   135 { SDL_SpinLock _tmp = 0; SDL_AtomicLock(&_tmp); SDL_AtomicUnlock(&_tmp); }
   136 #endif
   137 
   138 /* Platform specific optimized versions of the atomic functions,
   139  * you can disable these by defining SDL_DISABLE_ATOMIC_INLINE
   140  */
   141 #if defined(SDL_ATOMIC_DISABLED) && SDL_ATOMIC_DISABLED
   142 #define SDL_DISABLE_ATOMIC_INLINE
   143 #endif
   144 #ifndef SDL_DISABLE_ATOMIC_INLINE
   145 
   146 #ifdef HAVE_MSC_ATOMICS
   147 
   148 #define SDL_AtomicSet(a, v)     _InterlockedExchange((long*)&(a)->value, (v))
   149 #define SDL_AtomicAdd(a, v)     _InterlockedExchangeAdd((long*)&(a)->value, (v))
   150 #define SDL_AtomicCAS(a, oldval, newval) (_InterlockedCompareExchange((long*)&(a)->value, (newval), (oldval)) == (oldval))
   151 #define SDL_AtomicSetPtr(a, v)  _InterlockedExchangePointer((a), (v))
   152 #if _M_IX86
   153 #define SDL_AtomicCASPtr(a, oldval, newval) (_InterlockedCompareExchange((long*)(a), (long)(newval), (long)(oldval)) == (long)(oldval))
   154 #else
   155 #define SDL_AtomicCASPtr(a, oldval, newval) (_InterlockedCompareExchangePointer((a), (newval), (oldval)) == (oldval))
   156 #endif
   157 
   158 #elif defined(__MACOSX__)
   159 #include <libkern/OSAtomic.h>
   160 
   161 #define SDL_AtomicCAS(a, oldval, newval) OSAtomicCompareAndSwap32Barrier((oldval), (newval), &(a)->value)
   162 #ifdef __LP64__
   163 #define SDL_AtomicCASPtr(a, oldval, newval) OSAtomicCompareAndSwap64Barrier((int64_t)(oldval), (int64_t)(newval), (int64_t*)(a))
   164 #else
   165 #define SDL_AtomicCASPtr(a, oldval, newval) OSAtomicCompareAndSwap32Barrier((int32_t)(oldval), (int32_t)(newval), (int32_t*)(a))
   166 #endif
   167 
   168 #elif defined(HAVE_GCC_ATOMICS)
   169 
   170 #define SDL_AtomicSet(a, v)     __sync_lock_test_and_set(&(a)->value, v)
   171 #define SDL_AtomicAdd(a, v)     __sync_fetch_and_add(&(a)->value, v)
   172 #define SDL_AtomicSetPtr(a, v)  __sync_lock_test_and_set(a, v)
   173 #define SDL_AtomicCAS(a, oldval, newval) __sync_bool_compare_and_swap(&(a)->value, oldval, newval)
   174 #define SDL_AtomicCASPtr(a, oldval, newval) __sync_bool_compare_and_swap(a, oldval, newval)
   175 
   176 #endif
   177 
   178 #endif /* !SDL_DISABLE_ATOMIC_INLINE */
   179 
   180 
   181 /**
   182  * \brief A type representing an atomic integer value.  It is a struct
   183  *        so people don't accidentally use numeric operations on it.
   184  */
   185 #ifndef SDL_atomic_t_defined
   186 typedef struct { int value; } SDL_atomic_t;
   187 #endif
   188 
   189 /**
   190  * \brief Set an atomic variable to a new value if it is currently an old value.
   191  *
   192  * \return SDL_TRUE if the atomic variable was set, SDL_FALSE otherwise.
   193  *
   194  * \note If you don't know what this function is for, you shouldn't use it!
   195 */
   196 #ifndef SDL_AtomicCAS
   197 extern DECLSPEC SDL_bool SDLCALL SDL_AtomicCAS(SDL_atomic_t *a, int oldval, int newval);
   198 #endif
   199 
   200 /**
   201  * \brief Set an atomic variable to a value.
   202  *
   203  * \return The previous value of the atomic variable.
   204  */
   205 #ifndef SDL_AtomicSet
   206 SDL_FORCE_INLINE int SDL_AtomicSet(SDL_atomic_t *a, int v)
   207 {
   208     int value;
   209     do {
   210         value = a->value;
   211     } while (!SDL_AtomicCAS(a, value, v));
   212     return value;
   213 }
   214 #endif
   215 
   216 /**
   217  * \brief Get the value of an atomic variable
   218  */
   219 #ifndef SDL_AtomicGet
   220 SDL_FORCE_INLINE int SDL_AtomicGet(SDL_atomic_t *a)
   221 {
   222     int value = a->value;
   223     SDL_CompilerBarrier();
   224     return value;
   225 }
   226 #endif
   227 
   228 /**
   229  * \brief Add to an atomic variable.
   230  *
   231  * \return The previous value of the atomic variable.
   232  *
   233  * \note This same style can be used for any number operation
   234  */
   235 #ifndef SDL_AtomicAdd
   236 SDL_FORCE_INLINE int SDL_AtomicAdd(SDL_atomic_t *a, int v)
   237 {
   238     int value;
   239     do {
   240         value = a->value;
   241     } while (!SDL_AtomicCAS(a, value, (value + v)));
   242     return value;
   243 }
   244 #endif
   245 
   246 /**
   247  * \brief Increment an atomic variable used as a reference count.
   248  */
   249 #ifndef SDL_AtomicIncRef
   250 #define SDL_AtomicIncRef(a)    SDL_AtomicAdd(a, 1)
   251 #endif
   252 
   253 /**
   254  * \brief Decrement an atomic variable used as a reference count.
   255  *
   256  * \return SDL_TRUE if the variable reached zero after decrementing,
   257  *         SDL_FALSE otherwise
   258  */
   259 #ifndef SDL_AtomicDecRef
   260 #define SDL_AtomicDecRef(a)    (SDL_AtomicAdd(a, -1) == 1)
   261 #endif
   262 
   263 /**
   264  * \brief Set a pointer to a new value if it is currently an old value.
   265  *
   266  * \return SDL_TRUE if the pointer was set, SDL_FALSE otherwise.
   267  *
   268  * \note If you don't know what this function is for, you shouldn't use it!
   269 */
   270 #ifndef SDL_AtomicCASPtr
   271 extern DECLSPEC SDL_bool SDLCALL SDL_AtomicCASPtr(void* *a, void *oldval, void *newval);
   272 #endif
   273 
   274 /**
   275  * \brief Set a pointer to a value atomically.
   276  *
   277  * \return The previous value of the pointer.
   278  */
   279 #ifndef SDL_AtomicSetPtr
   280 SDL_FORCE_INLINE void* SDL_AtomicSetPtr(void* *a, void* v)
   281 {
   282     void* value;
   283     do {
   284         value = *a;
   285     } while (!SDL_AtomicCASPtr(a, value, v));
   286     return value;
   287 }
   288 #endif
   289 
   290 /**
   291  * \brief Get the value of a pointer atomically.
   292  */
   293 #ifndef SDL_AtomicGetPtr
   294 SDL_FORCE_INLINE void* SDL_AtomicGetPtr(void* *a)
   295 {
   296     void* value = *a;
   297     SDL_CompilerBarrier();
   298     return value;
   299 }
   300 #endif
   301 
   302 
   303 /* Ends C function definitions when using C++ */
   304 #ifdef __cplusplus
   305 }
   306 #endif
   307 
   308 #include "close_code.h"
   309 
   310 #endif /* _SDL_atomic_h_ */
   311 
   312 /* vi: set ts=4 sw=4 expandtab: */