include/SDL_atomic.h
changeset 5003 3a95a2b93eb3
parent 4474 119023057881
child 5004 0c72ae7b7cb2
     1.1 --- a/include/SDL_atomic.h	Sat Jan 15 12:34:43 2011 -0800
     1.2 +++ b/include/SDL_atomic.h	Sat Jan 15 12:41:59 2011 -0800
     1.3 @@ -18,24 +18,34 @@
     1.4  
     1.5      Sam Lantinga
     1.6      slouken@libsdl.org
     1.7 -
     1.8 -    Contributed by Bob Pendleton, bob@pendleton.com
     1.9   */
    1.10  
    1.11  /**
    1.12 - *  \file SDL_atomic.h
    1.13 - *  
    1.14 - *  Atomic operations.
    1.15 - *  
    1.16 - *  These operations may, or may not, actually be implemented using
    1.17 - *  processor specific atomic operations. When possible they are
    1.18 - *  implemented as true processor specific atomic operations. When that
    1.19 - *  is not possible the are implemented using locks that *do* use the
    1.20 - *  available atomic operations.
    1.21 - *  
    1.22 - *  At the very minimum spin locks must be implemented. Without spin
    1.23 - *  locks it is not possible (AFAICT) to emulate the rest of the atomic
    1.24 - *  operations.
    1.25 + * \file SDL_atomic.h
    1.26 + * 
    1.27 + * Atomic operations.
    1.28 + * 
    1.29 + * IMPORTANT:
    1.30 + * If you are not an expert in concurrent lockless programming, you should
    1.31 + * only be using the atomic lock and reference counting functions in this
    1.32 + * file.  In all other cases you should be protecting your data structures
    1.33 + * with full mutexes.
    1.34 + * 
    1.35 + * The list of "safe" functions to use are:
    1.36 + *  SDL_AtomicLock()
    1.37 + *  SDL_AtomicUnlock()
    1.38 + *  SDL_AtomicIncRef()
    1.39 + *  SDL_AtomicDecRef()
    1.40 + * 
    1.41 + * Seriously, here be dragons!
    1.42 + *
    1.43 + * These operations may, or may not, actually be implemented using
    1.44 + * processor specific atomic operations. When possible they are
    1.45 + * implemented as true processor specific atomic operations. When that
    1.46 + * is not possible the are implemented using locks that *do* use the
    1.47 + * available atomic operations.
    1.48 + *
    1.49 + * All of the atomic operations that modify memory are full memory barriers.
    1.50   */
    1.51  
    1.52  #ifndef _SDL_atomic_h_
    1.53 @@ -53,154 +63,138 @@
    1.54  /* *INDENT-ON* */
    1.55  #endif
    1.56  
    1.57 -/* Function prototypes */
    1.58 -
    1.59  /**
    1.60 - *  \name SDL AtomicLock
    1.61 - *  
    1.62 - *  The spin lock functions and type are required and can not be
    1.63 - *  emulated because they are used in the emulation code.
    1.64 + * \name SDL AtomicLock
    1.65 + * 
    1.66 + * The atomic locks are efficient spinlocks using CPU instructions,
    1.67 + * but are vulnerable to starvation and can spin forever if a thread
    1.68 + * holding a lock has been terminated.  For this reason you should
    1.69 + * minimize the code executed inside an atomic lock and never do
    1.70 + * expensive things like API or system calls while holding them.
    1.71 + *
    1.72 + * The atomic locks are not safe to lock recursively.
    1.73 + *
    1.74 + * Porting Note:
    1.75 + * The spin lock functions and type are required and can not be
    1.76 + * emulated because they are used in the atomic emulation code.
    1.77   */
    1.78  /*@{*/
    1.79  
    1.80 -typedef volatile Uint32 SDL_SpinLock;
    1.81 +typedef int SDL_SpinLock;
    1.82  
    1.83  /**
    1.84 - *  \brief Lock a spin lock by setting it to a none zero value.
    1.85 - *  
    1.86 - *  \param lock Points to the lock.
    1.87 + * \brief Try to lock a spin lock by setting it to a non-zero value.
    1.88 + * 
    1.89 + * \param lock Points to the lock.
    1.90 + *
    1.91 + * \return SDL_TRUE if the lock succeeded, SDL_FALSE if the lock is already held.
    1.92 + */
    1.93 +extern DECLSPEC SDL_bool SDLCALL SDL_AtomicTryLock(SDL_SpinLock *lock);
    1.94 +
    1.95 +/**
    1.96 + * \brief Lock a spin lock by setting it to a non-zero value.
    1.97 + * 
    1.98 + * \param lock Points to the lock.
    1.99   */
   1.100  extern DECLSPEC void SDLCALL SDL_AtomicLock(SDL_SpinLock *lock);
   1.101  
   1.102  /**
   1.103 - *  \brief Unlock a spin lock by setting it to 0. Always returns immediately
   1.104 + * \brief Unlock a spin lock by setting it to 0. Always returns immediately
   1.105   *
   1.106 - *  \param lock Points to the lock.
   1.107 + * \param lock Points to the lock.
   1.108   */
   1.109  extern DECLSPEC void SDLCALL SDL_AtomicUnlock(SDL_SpinLock *lock);
   1.110  
   1.111  /*@}*//*SDL AtomicLock*/
   1.112  
   1.113 -/**
   1.114 - *  \name 32 bit atomic operations
   1.115 - */
   1.116 -/*@{*/
   1.117 +/* Platform specific optimized versions of the atomic functions */
   1.118 +/* None yet... */
   1.119  
   1.120  /**
   1.121 - *  \brief Check to see if \c *ptr == 0 and set it to 1.
   1.122 - *  
   1.123 - *  \return SDL_True if the value pointed to by \c ptr was zero and
   1.124 - *          SDL_False if it was not zero
   1.125 - *  
   1.126 - *  \param ptr Points to the value to be tested and set.
   1.127 + * \brief A type representing an atomic integer value.  It is a struct
   1.128 + *        so people don't accidentally use numeric operations on it.
   1.129   */
   1.130 -extern DECLSPEC SDL_bool SDLCALL SDL_AtomicTestThenSet32(volatile Uint32 * ptr);
   1.131 +#ifndef SDL_atomic_t_defined
   1.132 +typedef struct { int value; } SDL_atomic_t;
   1.133 +#endif
   1.134  
   1.135  /**
   1.136 - *  \brief Set the value pointed to by \c ptr to be zero.
   1.137 - *  
   1.138 - *  \param ptr Address of the value to be set to zero
   1.139 + * \brief Set an atomic variable to a value.
   1.140 + *
   1.141 + * \return The previous value of the atomic variable.
   1.142   */
   1.143 -extern DECLSPEC void SDLCALL SDL_AtomicClear32(volatile Uint32 * ptr);
   1.144 +#ifndef SDL_AtomicSet
   1.145 +extern DECLSPEC int SDLCALL SDL_AtomicSet(SDL_atomic_t *a, int value);
   1.146 +#endif
   1.147  
   1.148  /**
   1.149 - *  \brief Fetch the current value of \c *ptr and then increment that
   1.150 - *         value in place.
   1.151 - *  
   1.152 - *  \return The value before it was incremented.
   1.153 - *  
   1.154 - *  \param ptr Address of the value to fetch and increment
   1.155 + * \brief Get the value of an atomic variable
   1.156   */
   1.157 -extern DECLSPEC Uint32 SDLCALL SDL_AtomicFetchThenIncrement32(volatile Uint32 * ptr);
   1.158 +#ifndef SDL_AtomicGet
   1.159 +extern DECLSPEC int SDLCALL SDL_AtomicGet(SDL_atomic_t *a);
   1.160 +#endif
   1.161  
   1.162  /**
   1.163 - *  \brief Fetch \c *ptr and then decrement the value in place.
   1.164 - *  
   1.165 - *  \return The value before it was decremented.
   1.166 - *  
   1.167 - *  \param ptr Address of the value to fetch and decrement
   1.168 + * \brief  Add to an atomic variable.
   1.169 + *
   1.170 + * \return The previous value of the atomic variable.
   1.171   */
   1.172 -extern DECLSPEC Uint32 SDLCALL SDL_AtomicFetchThenDecrement32(volatile Uint32 * ptr);
   1.173 +#ifndef SDL_AtomicAdd
   1.174 +extern DECLSPEC int SDLCALL SDL_AtomicAdd(SDL_atomic_t *a, int value);
   1.175 +#endif
   1.176  
   1.177  /**
   1.178 - *  \brief Fetch the current value at \c ptr and then add \c value to \c *ptr.
   1.179 - *  
   1.180 - *  \return \c *ptr before the addition took place.
   1.181 - *  
   1.182 - *  \param ptr The address of data we are changing.
   1.183 - *  \param value The value to add to \c *ptr. 
   1.184 + * \brief Increment an atomic variable used as a reference count.
   1.185   */
   1.186 -extern DECLSPEC Uint32 SDLCALL SDL_AtomicFetchThenAdd32(volatile Uint32 * ptr, Uint32 value);
   1.187 +#ifndef SDL_AtomicIncRef
   1.188 +extern DECLSPEC void SDLCALL SDL_AtomicIncRef(SDL_atomic_t *a);
   1.189 +#endif
   1.190  
   1.191  /**
   1.192 - *  \brief Fetch \c *ptr and then subtract \c value from it.
   1.193 - *  
   1.194 - *  \return \c *ptr before the subtraction took place.
   1.195 - *  
   1.196 - *  \param ptr The address of the data being changed.
   1.197 - *  \param value The value to subtract from \c *ptr.
   1.198 + * \brief Decrement an atomic variable used as a reference count.
   1.199 + *
   1.200 + * \return SDL_TRUE if the variable has reached zero after decrementing,
   1.201 + *         SDL_FALSE otherwise
   1.202   */
   1.203 -extern DECLSPEC Uint32 SDLCALL SDL_AtomicFetchThenSubtract32(volatile Uint32 * ptr, Uint32 value);
   1.204 +#ifndef SDL_AtomicDecRef
   1.205 +extern DECLSPEC SDL_bool SDLCALL SDL_AtomicDecRef(SDL_atomic_t *a);
   1.206 +#endif
   1.207  
   1.208  /**
   1.209 - *  \brief Add one to the data pointed to by \c ptr and return that value.
   1.210 - *  
   1.211 - *  \return The incremented value.
   1.212 - *  
   1.213 - *  \param ptr The address of the data to increment.
   1.214 - */
   1.215 -extern DECLSPEC Uint32 SDLCALL SDL_AtomicIncrementThenFetch32(volatile Uint32 * ptr);
   1.216 + * \brief Set an atomic variable to a new value if it is currently an old value.
   1.217 + *
   1.218 + * \return The previous value of the atomic variable
   1.219 + *
   1.220 + * \note If you don't know what this function is for, you shouldn't use it!
   1.221 +*/
   1.222 +#ifndef SDL_AtomicCAS
   1.223 +extern DECLSPEC int SDLCALL SDL_AtomicCAS(SDL_atomic_t *a, int oldval, int newval);
   1.224 +#endif
   1.225  
   1.226  /**
   1.227 - *  \brief Subtract one from data pointed to by \c ptr and return the new value.
   1.228 - *  
   1.229 - *  \return The decremented value.
   1.230 - *  
   1.231 - *  \param ptr The address of the data to decrement.
   1.232 + * \brief Set a pointer to a value atomically.
   1.233   */
   1.234 -extern DECLSPEC Uint32 SDLCALL SDL_AtomicDecrementThenFetch32(volatile Uint32 * ptr);
   1.235 +#ifndef SDL_AtomicSetPtr
   1.236 +extern DECLSPEC void SDLCALL SDL_AtomicSetPtr(void** a, void* value);
   1.237 +#endif
   1.238  
   1.239  /**
   1.240 - *  \brief Add \c value to the data pointed to by \c ptr and return result.
   1.241 - *  
   1.242 - *  \return The sum of \c *ptr and \c value.
   1.243 - *  
   1.244 - *  \param ptr The address of the data to be modified.
   1.245 - *  \param value The value to be added.
   1.246 + * \brief Get the value of a pointer atomically.
   1.247   */
   1.248 -extern DECLSPEC Uint32 SDLCALL SDL_AtomicAddThenFetch32(volatile Uint32 * ptr, Uint32 value);
   1.249 +#ifndef SDL_AtomicGetPtr
   1.250 +extern DECLSPEC void* SDLCALL SDL_AtomicGetPtr(void** a);
   1.251 +#endif
   1.252  
   1.253  /**
   1.254 - *  \brief Subtract \c value from the data pointed to by \c ptr and return the result.
   1.255 - *  
   1.256 - *  \return The difference between \c *ptr and \c value.
   1.257 - *  
   1.258 - *  \param ptr The address of the data to be modified.
   1.259 - *  \param value The value to be subtracted.
   1.260 - */
   1.261 -extern DECLSPEC Uint32 SDLCALL SDL_AtomicSubtractThenFetch32(volatile Uint32 * ptr, Uint32 value);
   1.262 -
   1.263 -/*@}*//*32 bit atomic operations*/
   1.264 -
   1.265 -/**
   1.266 - *  \name 64 bit atomic operations
   1.267 - */
   1.268 -/*@{*/
   1.269 -#ifdef SDL_HAS_64BIT_TYPE
   1.270 -
   1.271 -extern DECLSPEC SDL_bool SDLCALL SDL_AtomicTestThenSet64(volatile Uint64 * ptr);
   1.272 -extern DECLSPEC void SDLCALL SDL_AtomicClear64(volatile Uint64 * ptr);
   1.273 -extern DECLSPEC Uint64 SDLCALL SDL_AtomicFetchThenIncrement64(volatile Uint64 * ptr);
   1.274 -extern DECLSPEC Uint64 SDLCALL SDL_AtomicFetchThenDecrement64(volatile Uint64 * ptr);
   1.275 -extern DECLSPEC Uint64 SDLCALL SDL_AtomicFetchThenAdd64(volatile Uint64 * ptr, Uint64 value);
   1.276 -extern DECLSPEC Uint64 SDLCALL SDL_AtomicFetchThenSubtract64(volatile Uint64 * ptr, Uint64 value);
   1.277 -extern DECLSPEC Uint64 SDLCALL SDL_AtomicIncrementThenFetch64(volatile Uint64 * ptr);
   1.278 -extern DECLSPEC Uint64 SDLCALL SDL_AtomicDecrementThenFetch64(volatile Uint64 * ptr);
   1.279 -extern DECLSPEC Uint64 SDLCALL SDL_AtomicAddThenFetch64(volatile Uint64 * ptr, Uint64 value);
   1.280 -extern DECLSPEC Uint64 SDLCALL SDL_AtomicSubtractThenFetch64(volatile Uint64 * ptr, Uint64 value);
   1.281 -#endif /*  SDL_HAS_64BIT_TYPE */
   1.282 -
   1.283 -/*@}*//*64 bit atomic operations*/
   1.284 + * \brief Set a pointer to a new value if it is currently an old value.
   1.285 + *
   1.286 + * \return The previous value of the pointer
   1.287 + *
   1.288 + * \note If you don't know what this function is for, you shouldn't use it!
   1.289 +*/
   1.290 +#ifndef SDL_AtomicCASPtr
   1.291 +extern DECLSPEC void* SDLCALL SDL_AtomicCASPtr(void **a, void *oldval, void *newval);
   1.292 +#endif
   1.293  
   1.294  /* Ends C function definitions when using C++ */
   1.295  #ifdef __cplusplus