include/SDL_atomic.h
author Sam Lantinga <slouken@libsdl.org>
Fri, 21 Jan 2011 23:46:51 -0800
changeset 5073 1e94e68525d5
parent 5024 38638d228a22
child 5086 c2539ff054c8
permissions -rw-r--r--
Fixed bug #1097

There are problems with including intrin.h in C linkage in C++ compilation,
and further conflicts between intrin.h and winnt.h on Visual Studio 2005.
     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  * These operations may, or may not, actually be implemented using
    43  * processor specific atomic operations. When possible they are
    44  * implemented as true processor specific atomic operations. When that
    45  * is not possible the are implemented using locks that *do* use the
    46  * available atomic operations.
    47  *
    48  * All of the atomic operations that modify memory are full memory barriers.
    49  */
    50 
    51 #ifndef _SDL_atomic_h_
    52 #define _SDL_atomic_h_
    53 
    54 #include "SDL_stdinc.h"
    55 #include "SDL_platform.h"
    56 
    57 #include "begin_code.h"
    58 
    59 /* Need to do this here because intrin.h has C++ code in it */
    60 /* Visual Studio 2005 has a bug where intrin.h conflicts with winnt.h */
    61 #if defined(_MSC_VER) && (_MSC_VER >= 1500)
    62 #include <intrin.h>
    63 #define HAVE_MSC_ATOMICS
    64 #endif
    65 
    66 /* Set up for C function definitions, even when using C++ */
    67 #ifdef __cplusplus
    68 /* *INDENT-OFF* */
    69 extern "C" {
    70 /* *INDENT-ON* */
    71 #endif
    72 
    73 /**
    74  * \name SDL AtomicLock
    75  * 
    76  * The atomic locks are efficient spinlocks using CPU instructions,
    77  * but are vulnerable to starvation and can spin forever if a thread
    78  * holding a lock has been terminated.  For this reason you should
    79  * minimize the code executed inside an atomic lock and never do
    80  * expensive things like API or system calls while holding them.
    81  *
    82  * The atomic locks are not safe to lock recursively.
    83  *
    84  * Porting Note:
    85  * The spin lock functions and type are required and can not be
    86  * emulated because they are used in the atomic emulation code.
    87  */
    88 /*@{*/
    89 
    90 typedef int SDL_SpinLock;
    91 
    92 /**
    93  * \brief Try to lock a spin lock by setting it to a non-zero value.
    94  * 
    95  * \param lock Points to the lock.
    96  *
    97  * \return SDL_TRUE if the lock succeeded, SDL_FALSE if the lock is already held.
    98  */
    99 extern DECLSPEC SDL_bool SDLCALL SDL_AtomicTryLock(SDL_SpinLock *lock);
   100 
   101 /**
   102  * \brief Lock a spin lock by setting it to a non-zero value.
   103  * 
   104  * \param lock Points to the lock.
   105  */
   106 extern DECLSPEC void SDLCALL SDL_AtomicLock(SDL_SpinLock *lock);
   107 
   108 /**
   109  * \brief Unlock a spin lock by setting it to 0. Always returns immediately
   110  *
   111  * \param lock Points to the lock.
   112  */
   113 extern DECLSPEC void SDLCALL SDL_AtomicUnlock(SDL_SpinLock *lock);
   114 
   115 /*@}*//*SDL AtomicLock*/
   116 
   117 /* Platform specific optimized versions of the atomic functions,
   118  * you can disable these by defining SDL_DISABLE_ATOMIC_INLINE
   119  */
   120 #ifndef SDL_DISABLE_ATOMIC_INLINE
   121 
   122 #if defined(HAVE_MSC_ATOMICS)
   123 
   124 #define SDL_AtomicSet(a, v)     _InterlockedExchange((long*)&(a)->value, (v))
   125 #define SDL_AtomicGet(a)        ((a)->value)
   126 #define SDL_AtomicAdd(a, v)     _InterlockedExchangeAdd((long*)&(a)->value, (v))
   127 #define SDL_AtomicCAS(a, oldval, newval) (_InterlockedCompareExchange((long*)&(a)->value, (newval), (oldval)) == (oldval))
   128 #define SDL_AtomicSetPtr(a, v)  (void)_InterlockedExchangePointer((a), (v))
   129 #define SDL_AtomicGetPtr(a)     (*(a))
   130 #if _M_IX86
   131 #define SDL_AtomicCASPtr(a, oldval, newval) (_InterlockedCompareExchange((long*)(a), (long)(newval), (long)(oldval)) == (long)(oldval))
   132 #else
   133 #define SDL_AtomicCASPtr(a, oldval, newval) (_InterlockedCompareExchangePointer((a), (newval), (oldval)) == (oldval))
   134 #endif
   135 
   136 #elif defined(__MACOSX__)
   137 #include <libkern/OSAtomic.h>
   138 
   139 #define SDL_AtomicSet(a, v) \
   140 ({                          \
   141     int oldvalue;           \
   142                             \
   143     do {                    \
   144         oldvalue = (a)->value; \
   145     } while (!OSAtomicCompareAndSwap32Barrier(oldvalue, v, &(a)->value)); \
   146                             \
   147     oldvalue;               \
   148 })
   149 #define SDL_AtomicGet(a)        ((a)->value)
   150 #define SDL_AtomicAdd(a, v) \
   151 ({                          \
   152     int oldvalue;           \
   153                             \
   154     do {                    \
   155         oldvalue = (a)->value; \
   156     } while (!OSAtomicCompareAndSwap32Barrier(oldvalue, oldvalue+v, &(a)->value)); \
   157                             \
   158     oldvalue;               \
   159 })
   160 #define SDL_AtomicCAS(a, oldval, newval) OSAtomicCompareAndSwap32Barrier(oldval, newval, &(a)->value)
   161 #define SDL_AtomicSetPtr(a, v)  (*(a) = v, OSMemoryBarrier())
   162 #define SDL_AtomicGetPtr(a)     (*(a))
   163 #if SIZEOF_VOIDP == 4
   164 #define SDL_AtomicCASPtr(a, oldval, newval) OSAtomicCompareAndSwap32Barrier((int32_t)(oldval), (int32_t)(newval), (int32_t*)(a))
   165 #elif SIZEOF_VOIDP == 8
   166 #define SDL_AtomicCASPtr(a, oldval, newval) OSAtomicCompareAndSwap64Barrier((int64_t)(oldval), (int64_t)(newval), (int64_t*)(a))
   167 #endif
   168 
   169 #elif defined(HAVE_GCC_ATOMICS)
   170 
   171 #define SDL_AtomicSet(a, v)     __sync_lock_test_and_set(&(a)->value, v)
   172 #define SDL_AtomicGet(a)        ((a)->value)
   173 #define SDL_AtomicAdd(a, v)     __sync_fetch_and_add(&(a)->value, v)
   174 #define SDL_AtomicCAS(a, oldval, newval) __sync_bool_compare_and_swap(&(a)->value, oldval, newval)
   175 #define SDL_AtomicSetPtr(a, v)  (*(a) = v, __sync_synchronize())
   176 #define SDL_AtomicGetPtr(a)     (*(a))
   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 value.
   194  *
   195  * \return The previous value of the atomic variable.
   196  */
   197 #ifndef SDL_AtomicSet
   198 extern DECLSPEC int SDLCALL SDL_AtomicSet(SDL_atomic_t *a, int value);
   199 #endif
   200 
   201 /**
   202  * \brief Get the value of an atomic variable
   203  */
   204 #ifndef SDL_AtomicGet
   205 extern DECLSPEC int SDLCALL SDL_AtomicGet(SDL_atomic_t *a);
   206 #endif
   207 
   208 /**
   209  * \brief  Add to an atomic variable.
   210  *
   211  * \return The previous value of the atomic variable.
   212  */
   213 #ifndef SDL_AtomicAdd
   214 extern DECLSPEC int SDLCALL SDL_AtomicAdd(SDL_atomic_t *a, int value);
   215 #endif
   216 
   217 /**
   218  * \brief Increment an atomic variable used as a reference count.
   219  */
   220 #ifndef SDL_AtomicIncRef
   221 extern DECLSPEC void SDLCALL SDL_AtomicIncRef(SDL_atomic_t *a);
   222 #endif
   223 
   224 /**
   225  * \brief Decrement an atomic variable used as a reference count.
   226  *
   227  * \return SDL_TRUE if the variable has reached zero after decrementing,
   228  *         SDL_FALSE otherwise
   229  */
   230 #ifndef SDL_AtomicDecRef
   231 extern DECLSPEC SDL_bool SDLCALL SDL_AtomicDecRef(SDL_atomic_t *a);
   232 #endif
   233 
   234 /**
   235  * \brief Set an atomic variable to a new value if it is currently an old value.
   236  *
   237  * \return SDL_TRUE if the atomic variable was set, SDL_FALSE otherwise.
   238  *
   239  * \note If you don't know what this function is for, you shouldn't use it!
   240 */
   241 #ifndef SDL_AtomicCAS
   242 extern DECLSPEC SDL_bool SDLCALL SDL_AtomicCAS(SDL_atomic_t *a, int oldval, int newval);
   243 #endif
   244 
   245 /**
   246  * \brief Set a pointer to a value atomically.
   247  */
   248 #ifndef SDL_AtomicSetPtr
   249 extern DECLSPEC void SDLCALL SDL_AtomicSetPtr(void** a, void* value);
   250 #endif
   251 
   252 /**
   253  * \brief Get the value of a pointer atomically.
   254  */
   255 #ifndef SDL_AtomicGetPtr
   256 extern DECLSPEC void* SDLCALL SDL_AtomicGetPtr(void** a);
   257 #endif
   258 
   259 /**
   260  * \brief Set a pointer to a new value if it is currently an old value.
   261  *
   262  * \return SDL_TRUE if the pointer was set, SDL_FALSE otherwise.
   263  *
   264  * \note If you don't know what this function is for, you shouldn't use it!
   265 */
   266 #ifndef SDL_AtomicCASPtr
   267 extern DECLSPEC SDL_bool SDLCALL SDL_AtomicCASPtr(void **a, void *oldval, void *newval);
   268 #endif
   269 
   270 /* Ends C function definitions when using C++ */
   271 #ifdef __cplusplus
   272 /* *INDENT-OFF* */
   273 }
   274 /* *INDENT-ON* */
   275 #endif
   276 
   277 #include "close_code.h"
   278 
   279 #endif /* _SDL_atomic_h_ */
   280 
   281 /* vi: set ts=4 sw=4 expandtab: */