include/SDL_atomic.h
author Sam Lantinga <slouken@libsdl.org>
Sun, 16 Jan 2011 17:45:42 -0800
changeset 5006 8e8876e4aec6
parent 5005 7f0265279b68
child 5012 2e282002bac3
permissions -rw-r--r--
Include windows.h in SDL_atomic.h by default, but don't include the atomic API in SDL.h
This allows all SDL code to take advantage of the atomic intrinsics on Windows, but doesn't cause applications just including SDL.h to pull in windows.h
     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 /* Set up for C function definitions, even when using C++ */
    60 #ifdef __cplusplus
    61 /* *INDENT-OFF* */
    62 extern "C" {
    63 /* *INDENT-ON* */
    64 #endif
    65 
    66 /**
    67  * \name SDL AtomicLock
    68  * 
    69  * The atomic locks are efficient spinlocks using CPU instructions,
    70  * but are vulnerable to starvation and can spin forever if a thread
    71  * holding a lock has been terminated.  For this reason you should
    72  * minimize the code executed inside an atomic lock and never do
    73  * expensive things like API or system calls while holding them.
    74  *
    75  * The atomic locks are not safe to lock recursively.
    76  *
    77  * Porting Note:
    78  * The spin lock functions and type are required and can not be
    79  * emulated because they are used in the atomic emulation code.
    80  */
    81 /*@{*/
    82 
    83 typedef int SDL_SpinLock;
    84 
    85 /**
    86  * \brief Try to lock a spin lock by setting it to a non-zero value.
    87  * 
    88  * \param lock Points to the lock.
    89  *
    90  * \return SDL_TRUE if the lock succeeded, SDL_FALSE if the lock is already held.
    91  */
    92 extern DECLSPEC SDL_bool SDLCALL SDL_AtomicTryLock(SDL_SpinLock *lock);
    93 
    94 /**
    95  * \brief Lock a spin lock by setting it to a non-zero value.
    96  * 
    97  * \param lock Points to the lock.
    98  */
    99 extern DECLSPEC void SDLCALL SDL_AtomicLock(SDL_SpinLock *lock);
   100 
   101 /**
   102  * \brief Unlock a spin lock by setting it to 0. Always returns immediately
   103  *
   104  * \param lock Points to the lock.
   105  */
   106 extern DECLSPEC void SDLCALL SDL_AtomicUnlock(SDL_SpinLock *lock);
   107 
   108 /*@}*//*SDL AtomicLock*/
   109 
   110 /* Platform specific optimized versions of the atomic functions,
   111  * you can disable these by defining SDL_DISABLE_ATOMIC_INLINE
   112  */
   113 #ifndef SDL_DISABLE_ATOMIC_INLINE
   114 
   115 #if defined(__WIN32__)
   116 /* Don't include windows.h, since it may hose code that isn't expecting it,
   117    but if someone has already included it, this is fair game... */
   118 #define WIN32_LEAN_AND_MEAN
   119 #include <windows.h>
   120 
   121 #define SDL_AtomicSet(a, v)     InterlockedExchange(&(a)->value, v)
   122 #define SDL_AtomicGet(a)        ((a)->value)
   123 #define SDL_AtomicAdd(a, v)     InterlockedAdd(&(a)->value, v)
   124 #define SDL_AtomicCAS(a, oldval, newval) (InterlockedCompareExchange(&(a)->value, newval, oldval) == (oldval))
   125 #define SDL_AtomicSetPtr(a, v)  InterlockedExchangePointer(a, v)
   126 #define SDL_AtomicGetPtr(a)     (*(a))
   127 #define SDL_AtomicCASPtr(a, oldval, newval) (InterlockedCompareExchangePointer(a, newval, oldval) == (oldval))
   128 
   129 #elif defined(__MACOSX__)
   130 #include <libkern/OSAtomic.h>
   131 
   132 #define SDL_AtomicSet(a, v) \
   133 ({                          \
   134     int oldvalue;           \
   135                             \
   136     do {                    \
   137         oldvalue = (a)->value; \
   138     } while (!OSAtomicCompareAndSwap32Barrier(oldvalue, v, &(a)->value)); \
   139                             \
   140     oldvalue;               \
   141 })
   142 #define SDL_AtomicGet(a)        ((a)->value)
   143 #define SDL_AtomicAdd(a, v) \
   144 ({                          \
   145     int oldvalue;           \
   146                             \
   147     do {                    \
   148         oldvalue = (a)->value; \
   149     } while (!OSAtomicCompareAndSwap32Barrier(oldvalue, oldvalue+v, &(a)->value)); \
   150                             \
   151     oldvalue;               \
   152 })
   153 #define SDL_AtomicCAS(a, oldval, newval) OSAtomicCompareAndSwap32Barrier(oldval, newval, &(a)->value)
   154 #define SDL_AtomicSetPtr(a, v)  (*(a) = v, OSMemoryBarrier())
   155 #define SDL_AtomicGetPtr(a)     (*(a))
   156 #if SIZEOF_VOIDP == 4
   157 #define SDL_AtomicCASPtr(a, oldval, newval) OSAtomicCompareAndSwap32Barrier((int32_t)(oldval), (int32_t)(newval), (int32_t*)(a))
   158 #elif SIZEOF_VOIDP == 8
   159 #define SDL_AtomicCASPtr(a, oldval, newval) OSAtomicCompareAndSwap64Barrier((int64_t)(oldval), (int64_t)(newval), (int64_t*)(a))
   160 #endif
   161 
   162 #elif defined(HAVE_GCC_ATOMICS)
   163 
   164 #define SDL_AtomicSet(a, v)     __sync_lock_test_and_set(&(a)->value, v)
   165 #define SDL_AtomicGet(a)        ((a)->value)
   166 #define SDL_AtomicAdd(a, v)     __sync_fetch_and_add(&(a)->value, v)
   167 #define SDL_AtomicCAS(a, oldval, newval) __sync_bool_compare_and_swap(&(a)->value, oldval, newval)
   168 #define SDL_AtomicSetPtr(a, v)  (*(a) = v, __sync_synchronize())
   169 #define SDL_AtomicGetPtr(a)     (*(a))
   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 extern DECLSPEC int SDLCALL SDL_AtomicSet(SDL_atomic_t *a, int value);
   192 #endif
   193 
   194 /**
   195  * \brief Get the value of an atomic variable
   196  */
   197 #ifndef SDL_AtomicGet
   198 extern DECLSPEC int SDLCALL SDL_AtomicGet(SDL_atomic_t *a);
   199 #endif
   200 
   201 /**
   202  * \brief  Add to an atomic variable.
   203  *
   204  * \return The previous value of the atomic variable.
   205  */
   206 #ifndef SDL_AtomicAdd
   207 extern DECLSPEC int SDLCALL SDL_AtomicAdd(SDL_atomic_t *a, int value);
   208 #endif
   209 
   210 /**
   211  * \brief Increment an atomic variable used as a reference count.
   212  */
   213 #ifndef SDL_AtomicIncRef
   214 extern DECLSPEC void SDLCALL SDL_AtomicIncRef(SDL_atomic_t *a);
   215 #endif
   216 
   217 /**
   218  * \brief Decrement an atomic variable used as a reference count.
   219  *
   220  * \return SDL_TRUE if the variable has reached zero after decrementing,
   221  *         SDL_FALSE otherwise
   222  */
   223 #ifndef SDL_AtomicDecRef
   224 extern DECLSPEC SDL_bool SDLCALL SDL_AtomicDecRef(SDL_atomic_t *a);
   225 #endif
   226 
   227 /**
   228  * \brief Set an atomic variable to a new value if it is currently an old value.
   229  *
   230  * \return SDL_TRUE if the atomic variable was set, SDL_FALSE otherwise.
   231  *
   232  * \note If you don't know what this function is for, you shouldn't use it!
   233 */
   234 #ifndef SDL_AtomicCAS
   235 extern DECLSPEC SDL_bool SDLCALL SDL_AtomicCAS(SDL_atomic_t *a, int oldval, int newval);
   236 #endif
   237 
   238 /**
   239  * \brief Set a pointer to a value atomically.
   240  */
   241 #ifndef SDL_AtomicSetPtr
   242 extern DECLSPEC void SDLCALL SDL_AtomicSetPtr(void** a, void* value);
   243 #endif
   244 
   245 /**
   246  * \brief Get the value of a pointer atomically.
   247  */
   248 #ifndef SDL_AtomicGetPtr
   249 extern DECLSPEC void* SDLCALL SDL_AtomicGetPtr(void** a);
   250 #endif
   251 
   252 /**
   253  * \brief Set a pointer to a new value if it is currently an old value.
   254  *
   255  * \return SDL_TRUE if the pointer was set, SDL_FALSE otherwise.
   256  *
   257  * \note If you don't know what this function is for, you shouldn't use it!
   258 */
   259 #ifndef SDL_AtomicCASPtr
   260 extern DECLSPEC SDL_bool SDLCALL SDL_AtomicCASPtr(void **a, void *oldval, void *newval);
   261 #endif
   262 
   263 /* Ends C function definitions when using C++ */
   264 #ifdef __cplusplus
   265 /* *INDENT-OFF* */
   266 }
   267 /* *INDENT-ON* */
   268 #endif
   269 
   270 #include "close_code.h"
   271 
   272 #endif /* _SDL_atomic_h_ */
   273 
   274 /* vi: set ts=4 sw=4 expandtab: */