include/SDL_atomic.h
author Sam Lantinga <slouken@libsdl.org>
Tue, 25 Jan 2011 17:44:17 -0800
changeset 5096 e4301cde4de1
parent 5095 dceec93471e7
child 5097 b938ad843e52
permissions -rw-r--r--
Fixed compile error on Visual C++
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2010 Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Lesser General Public
     7     License as published by the Free Software Foundation; either
     8     version 2.1 of the License, or (at your option) any later version.
     9 
    10     This library is distributed in the hope that it will be useful,
    11     but WITHOUT ANY WARRANTY; without even the implied warranty of
    12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13     Lesser General Public License for more details.
    14 
    15     You should have received a copy of the GNU Lesser General Public
    16     License along with this library; if not, write to the Free Software
    17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21  */
    22 
    23 /**
    24  * \file SDL_atomic.h
    25  * 
    26  * Atomic operations.
    27  * 
    28  * IMPORTANT:
    29  * If you are not an expert in concurrent lockless programming, you should
    30  * only be using the atomic lock and reference counting functions in this
    31  * file.  In all other cases you should be protecting your data structures
    32  * with full mutexes.
    33  * 
    34  * The list of "safe" functions to use are:
    35  *  SDL_AtomicLock()
    36  *  SDL_AtomicUnlock()
    37  *  SDL_AtomicIncRef()
    38  *  SDL_AtomicDecRef()
    39  * 
    40  * Seriously, here be dragons!
    41  * ^^^^^^^^^^^^^^^^^^^^^^^^^^^
    42  *
    43  * You can find out a little more about lockless programming and the 
    44  * subtle issues that can arise here:
    45  * http://msdn.microsoft.com/en-us/library/ee418650%28v=vs.85%29.aspx
    46  *
    47  * These operations may or may not actually be implemented using
    48  * processor specific atomic operations. When possible they are
    49  * implemented as true processor specific atomic operations. When that
    50  * is not possible the are implemented using locks that *do* use the
    51  * available atomic operations.
    52  *
    53  * All of the atomic operations that modify memory are full memory barriers.
    54  */
    55 
    56 #ifndef _SDL_atomic_h_
    57 #define _SDL_atomic_h_
    58 
    59 #include "SDL_stdinc.h"
    60 #include "SDL_platform.h"
    61 
    62 #include "begin_code.h"
    63 
    64 /* Need to do this here because intrin.h has C++ code in it */
    65 /* Visual Studio 2005 has a bug where intrin.h conflicts with winnt.h */
    66 #if defined(_MSC_VER) && (_MSC_VER >= 1500) && !defined(_WIN32_WCE)
    67 #include <intrin.h>
    68 #define HAVE_MSC_ATOMICS 1
    69 #endif
    70 
    71 /* Set up for C function definitions, even when using C++ */
    72 #ifdef __cplusplus
    73 /* *INDENT-OFF* */
    74 extern "C" {
    75 /* *INDENT-ON* */
    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 /* The compiler barrier prevents the compiler from reordering
   124    reads and writes to globally visible variables across the call.
   125 */
   126 #ifdef _MSC_VER
   127 void _ReadWriteBarrier(void);
   128 #pragma intrinsic(_ReadWriteBarrier)
   129 #define SDL_CompilerBarrier()   _ReadWriteBarrier()
   130 #elif __GNUC__
   131 #define SDL_CompilerBarrier()   __asm__ __volatile__ ("" : : : "memory")
   132 #else
   133 #define SDL_CompilerBarrier()   \
   134 ({ SDL_SpinLock _tmp = 0; SDL_AtomicLock(&_tmp); SDL_AtomicUnlock(&_tmp); })
   135 #endif
   136 
   137 /* Platform specific optimized versions of the atomic functions,
   138  * you can disable these by defining SDL_DISABLE_ATOMIC_INLINE
   139  */
   140 #ifndef SDL_DISABLE_ATOMIC_INLINE
   141 
   142 #if HAVE_MSC_ATOMICS
   143 
   144 #define SDL_AtomicSet(a, v)     _InterlockedExchange((long*)&(a)->value, (v))
   145 #define SDL_AtomicAdd(a, v)     _InterlockedExchangeAdd((long*)&(a)->value, (v))
   146 #define SDL_AtomicCAS(a, oldval, newval) (_InterlockedCompareExchange((long*)&(a)->value, (newval), (oldval)) == (oldval))
   147 #define SDL_AtomicSetPtr(a, v)  _InterlockedExchangePointer((a), (v))
   148 #if _M_IX86
   149 #define SDL_AtomicCASPtr(a, oldval, newval) (_InterlockedCompareExchange((long*)(a), (long)(newval), (long)(oldval)) == (long)(oldval))
   150 #else
   151 #define SDL_AtomicCASPtr(a, oldval, newval) (_InterlockedCompareExchangePointer((a), (newval), (oldval)) == (oldval))
   152 #endif
   153 
   154 #elif __MACOSX__
   155 #include <libkern/OSAtomic.h>
   156 
   157 #define SDL_AtomicCAS(a, oldval, newval) OSAtomicCompareAndSwap32Barrier((oldval), (newval), &(a)->value)
   158 #if SIZEOF_VOIDP == 4
   159 #define SDL_AtomicCASPtr(a, oldval, newval) OSAtomicCompareAndSwap32Barrier((int32_t)(oldval), (int32_t)(newval), (int32_t*)(a))
   160 #elif SIZEOF_VOIDP == 8
   161 #define SDL_AtomicCASPtr(a, oldval, newval) OSAtomicCompareAndSwap64Barrier((int64_t)(oldval), (int64_t)(newval), (int64_t*)(a))
   162 #endif
   163 
   164 #elif HAVE_GCC_ATOMICS
   165 
   166 #define SDL_AtomicSet(a, v)     __sync_lock_test_and_set(&(a)->value, v)
   167 #define SDL_AtomicAdd(a, v)     __sync_fetch_and_add(&(a)->value, v)
   168 #define SDL_AtomicSetPtr(a, v)  __sync_lock_test_and_set(a, v)
   169 #define SDL_AtomicCAS(a, oldval, newval) __sync_bool_compare_and_swap(&(a)->value, oldval, newval)
   170 #define SDL_AtomicCASPtr(a, oldval, newval) __sync_bool_compare_and_swap(a, oldval, newval)
   171 
   172 #endif
   173 
   174 #endif /* !SDL_DISABLE_ATOMIC_INLINE */
   175 
   176 
   177 /**
   178  * \brief A type representing an atomic integer value.  It is a struct
   179  *        so people don't accidentally use numeric operations on it.
   180  */
   181 #ifndef SDL_atomic_t_defined
   182 typedef struct { int value; } SDL_atomic_t;
   183 #endif
   184 
   185 /**
   186  * \brief Set an atomic variable to a value.
   187  *
   188  * \return The previous value of the atomic variable.
   189  */
   190 #ifndef SDL_AtomicSet
   191 #define SDL_AtomicSet(a, v) \
   192 ({                              \
   193     int _value;                 \
   194     do {                        \
   195         _value = (a)->value;    \
   196     } while (!SDL_AtomicCAS(a, _value, (v))); \
   197     _value;                     \
   198 })
   199 #endif
   200 
   201 /**
   202  * \brief Get the value of an atomic variable
   203  */
   204 #ifndef SDL_AtomicGet
   205 #define SDL_AtomicGet(a)        \
   206 ({                              \
   207     int _value = (a)->value;    \
   208     SDL_CompilerBarrier();      \
   209     _value;                     \
   210 })
   211 #endif
   212 
   213 /**
   214  * \brief Add to an atomic variable.
   215  *
   216  * \return The previous value of the atomic variable.
   217  *
   218  * \note This same style can be used for any number operation
   219  */
   220 #ifndef SDL_AtomicAdd
   221 #define SDL_AtomicAdd(a, v)     \
   222 ({                              \
   223     int _value;                 \
   224     do {                        \
   225         _value = (a)->value;    \
   226     } while (!SDL_AtomicCAS(a, _value, (_value + (v)))); \
   227     _value;                     \
   228 })
   229 #endif
   230 
   231 /**
   232  * \brief Increment an atomic variable used as a reference count.
   233  */
   234 #ifndef SDL_AtomicIncRef
   235 #define SDL_AtomicIncRef(a)    SDL_AtomicAdd(a, 1)
   236 #endif
   237 
   238 /**
   239  * \brief Decrement an atomic variable used as a reference count.
   240  *
   241  * \return SDL_TRUE if the variable reached zero after decrementing,
   242  *         SDL_FALSE otherwise
   243  */
   244 #ifndef SDL_AtomicDecRef
   245 #define SDL_AtomicDecRef(a)    (SDL_AtomicAdd(a, -1) == 1)
   246 #endif
   247 
   248 /**
   249  * \brief Set an atomic variable to a new value if it is currently an old value.
   250  *
   251  * \return SDL_TRUE if the atomic variable was set, SDL_FALSE otherwise.
   252  *
   253  * \note If you don't know what this function is for, you shouldn't use it!
   254 */
   255 #ifndef SDL_AtomicCAS
   256 #define SDL_AtomicCAS SDL_AtomicCAS_
   257 #endif
   258 extern DECLSPEC SDL_bool SDLCALL SDL_AtomicCAS_(SDL_atomic_t *a, int oldval, int newval);
   259 
   260 /**
   261  * \brief Set a pointer to a value atomically.
   262  *
   263  * \return The previous value of the pointer.
   264  */
   265 #ifndef SDL_AtomicSetPtr
   266 #define SDL_AtomicSetPtr(a, v)  \
   267 ({                              \
   268     void* _value;               \
   269     do {                        \
   270         _value = *(a);          \
   271     } while (!SDL_AtomicCASPtr(a, _value, (v))); \
   272     _value;                     \
   273 })
   274 #endif
   275 
   276 /**
   277  * \brief Get the value of a pointer atomically.
   278  */
   279 #ifndef SDL_AtomicGetPtr
   280 #define SDL_AtomicGetPtr(a)     \
   281 ({                              \
   282     void* _value = *(a);        \
   283     SDL_CompilerBarrier();      \
   284     _value;                     \
   285 })
   286 #endif
   287 
   288 /**
   289  * \brief Set a pointer to a new value if it is currently an old value.
   290  *
   291  * \return SDL_TRUE if the pointer was set, SDL_FALSE otherwise.
   292  *
   293  * \note If you don't know what this function is for, you shouldn't use it!
   294 */
   295 #ifndef SDL_AtomicCASPtr
   296 #define SDL_AtomicCASPtr SDL_AtomicCASPtr_
   297 #endif
   298 extern DECLSPEC SDL_bool SDLCALL SDL_AtomicCASPtr_(void **a, void *oldval, void *newval);
   299 
   300 /* Ends C function definitions when using C++ */
   301 #ifdef __cplusplus
   302 /* *INDENT-OFF* */
   303 }
   304 /* *INDENT-ON* */
   305 #endif
   306 
   307 #include "close_code.h"
   308 
   309 #endif /* _SDL_atomic_h_ */
   310 
   311 /* vi: set ts=4 sw=4 expandtab: */