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