include/SDL_atomic.h
author Ryan C. Gordon <icculus@icculus.org>
Sat, 15 Sep 2012 10:59:39 -0400
changeset 6430 48d519500f7e
parent 6138 4c64952a58fb
child 6463 051403ca44cc
permissions -rw-r--r--
Removed Windows CE support from SDL 2.0.

It's a long-dead platform, and we don't have any way to build for, test, or
maintain it, so there's no sense in doing acrobatics to support it.

If you need Windows CE support, use SDL 1.2. If you need Windows Phone support,
send SDL 2.0 patches for the newer Windows Mobile platform.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2012 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 #ifdef _MSC_VER
   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 #if SIZEOF_VOIDP == 4
   165 #define SDL_AtomicCASPtr(a, oldval, newval) OSAtomicCompareAndSwap32Barrier((int32_t)(oldval), (int32_t)(newval), (int32_t*)(a))
   166 #elif SIZEOF_VOIDP == 8
   167 #define SDL_AtomicCASPtr(a, oldval, newval) OSAtomicCompareAndSwap64Barrier((int64_t)(oldval), (int64_t)(newval), (int64_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 #define SDL_AtomicCAS SDL_AtomicCAS_
   200 #endif
   201 extern DECLSPEC SDL_bool SDLCALL SDL_AtomicCAS_(SDL_atomic_t *a, int oldval, int newval);
   202 
   203 /**
   204  * \brief Set an atomic variable to a value.
   205  *
   206  * \return The previous value of the atomic variable.
   207  */
   208 #ifndef SDL_AtomicSet
   209 static __inline__ int SDL_AtomicSet(SDL_atomic_t *a, int v)
   210 {
   211     int value;
   212     do {
   213         value = a->value;
   214     } while (!SDL_AtomicCAS(a, value, v));
   215     return value;
   216 }
   217 #endif
   218 
   219 /**
   220  * \brief Get the value of an atomic variable
   221  */
   222 #ifndef SDL_AtomicGet
   223 static __inline__ int SDL_AtomicGet(SDL_atomic_t *a)
   224 {
   225     int value = a->value;
   226     SDL_CompilerBarrier();
   227     return value;
   228 }
   229 #endif
   230 
   231 /**
   232  * \brief Add to an atomic variable.
   233  *
   234  * \return The previous value of the atomic variable.
   235  *
   236  * \note This same style can be used for any number operation
   237  */
   238 #ifndef SDL_AtomicAdd
   239 static __inline__ int SDL_AtomicAdd(SDL_atomic_t *a, int v)
   240 {
   241     int value;
   242     do {
   243         value = a->value;
   244     } while (!SDL_AtomicCAS(a, value, (value + v)));
   245     return value;
   246 }
   247 #endif
   248 
   249 /**
   250  * \brief Increment an atomic variable used as a reference count.
   251  */
   252 #ifndef SDL_AtomicIncRef
   253 #define SDL_AtomicIncRef(a)    SDL_AtomicAdd(a, 1)
   254 #endif
   255 
   256 /**
   257  * \brief Decrement an atomic variable used as a reference count.
   258  *
   259  * \return SDL_TRUE if the variable reached zero after decrementing,
   260  *         SDL_FALSE otherwise
   261  */
   262 #ifndef SDL_AtomicDecRef
   263 #define SDL_AtomicDecRef(a)    (SDL_AtomicAdd(a, -1) == 1)
   264 #endif
   265 
   266 /**
   267  * \brief Set a pointer to a new value if it is currently an old value.
   268  *
   269  * \return SDL_TRUE if the pointer was set, SDL_FALSE otherwise.
   270  *
   271  * \note If you don't know what this function is for, you shouldn't use it!
   272 */
   273 #ifndef SDL_AtomicCASPtr
   274 #define SDL_AtomicCASPtr SDL_AtomicCASPtr_
   275 #endif
   276 extern DECLSPEC SDL_bool SDLCALL SDL_AtomicCASPtr_(void* *a, void *oldval, void *newval);
   277 
   278 /**
   279  * \brief Set a pointer to a value atomically.
   280  *
   281  * \return The previous value of the pointer.
   282  */
   283 #ifndef SDL_AtomicSetPtr
   284 static __inline__ void* SDL_AtomicSetPtr(void* *a, void* v)
   285 {
   286     void* value;
   287     do {
   288         value = *a;
   289     } while (!SDL_AtomicCASPtr(a, value, v));
   290     return value;
   291 }
   292 #endif
   293 
   294 /**
   295  * \brief Get the value of a pointer atomically.
   296  */
   297 #ifndef SDL_AtomicGetPtr
   298 static __inline__ void* SDL_AtomicGetPtr(void* *a)
   299 {
   300     void* value = *a;
   301     SDL_CompilerBarrier();
   302     return value;
   303 }
   304 #endif
   305 
   306 
   307 /* Ends C function definitions when using C++ */
   308 #ifdef __cplusplus
   309 /* *INDENT-OFF* */
   310 }
   311 /* *INDENT-ON* */
   312 #endif
   313 
   314 #include "close_code.h"
   315 
   316 #endif /* _SDL_atomic_h_ */
   317 
   318 /* vi: set ts=4 sw=4 expandtab: */