comparison include/SDL_atomic.h @ 5097:dceec93471e7

Improvements based on feedback from Anthony Williams
author Sam Lantinga <slouken@libsdl.org>
date Tue, 25 Jan 2011 17:40:06 -0800
parents c2539ff054c8
children e4301cde4de1
comparison
equal deleted inserted replaced
5096:124cda437b07 5097:dceec93471e7
36 * SDL_AtomicUnlock() 36 * SDL_AtomicUnlock()
37 * SDL_AtomicIncRef() 37 * SDL_AtomicIncRef()
38 * SDL_AtomicDecRef() 38 * SDL_AtomicDecRef()
39 * 39 *
40 * Seriously, here be dragons! 40 * Seriously, here be dragons!
41 * 41 * ^^^^^^^^^^^^^^^^^^^^^^^^^^^
42 * These operations may, or may not, actually be implemented using 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
43 * processor specific atomic operations. When possible they are 48 * processor specific atomic operations. When possible they are
44 * implemented as true processor specific atomic operations. When that 49 * implemented as true processor specific atomic operations. When that
45 * is not possible the are implemented using locks that *do* use the 50 * is not possible the are implemented using locks that *do* use the
46 * available atomic operations. 51 * available atomic operations.
47 * 52 *
112 */ 117 */
113 extern DECLSPEC void SDLCALL SDL_AtomicUnlock(SDL_SpinLock *lock); 118 extern DECLSPEC void SDLCALL SDL_AtomicUnlock(SDL_SpinLock *lock);
114 119
115 /*@}*//*SDL AtomicLock*/ 120 /*@}*//*SDL AtomicLock*/
116 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
117 /* Platform specific optimized versions of the atomic functions, 137 /* Platform specific optimized versions of the atomic functions,
118 * you can disable these by defining SDL_DISABLE_ATOMIC_INLINE 138 * you can disable these by defining SDL_DISABLE_ATOMIC_INLINE
119 */ 139 */
120 #ifndef SDL_DISABLE_ATOMIC_INLINE 140 #ifndef SDL_DISABLE_ATOMIC_INLINE
121 141
122 #if defined(HAVE_MSC_ATOMICS) 142 #if HAVE_MSC_ATOMICS
123 143
124 #define SDL_AtomicSet(a, v) _InterlockedExchange((long*)&(a)->value, (v)) 144 #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)) 145 #define SDL_AtomicAdd(a, v) _InterlockedExchangeAdd((long*)&(a)->value, (v))
127 #define SDL_AtomicCAS(a, oldval, newval) (_InterlockedCompareExchange((long*)&(a)->value, (newval), (oldval)) == (oldval)) 146 #define SDL_AtomicCAS(a, oldval, newval) (_InterlockedCompareExchange((long*)&(a)->value, (newval), (oldval)) == (oldval))
128 #define SDL_AtomicSetPtr(a, v) (void)_InterlockedExchangePointer((a), (v)) 147 #define SDL_AtomicSetPtr(a, v) _InterlockedExchangePointer((a), (v))
129 #define SDL_AtomicGetPtr(a) (*(a))
130 #if _M_IX86 148 #if _M_IX86
131 #define SDL_AtomicCASPtr(a, oldval, newval) (_InterlockedCompareExchange((long*)(a), (long)(newval), (long)(oldval)) == (long)(oldval)) 149 #define SDL_AtomicCASPtr(a, oldval, newval) (_InterlockedCompareExchange((long*)(a), (long)(newval), (long)(oldval)) == (long)(oldval))
132 #else 150 #else
133 #define SDL_AtomicCASPtr(a, oldval, newval) (_InterlockedCompareExchangePointer((a), (newval), (oldval)) == (oldval)) 151 #define SDL_AtomicCASPtr(a, oldval, newval) (_InterlockedCompareExchangePointer((a), (newval), (oldval)) == (oldval))
134 #endif 152 #endif
135 153
136 #elif defined(__MACOSX__) 154 #elif __MACOSX__
137 #include <libkern/OSAtomic.h> 155 #include <libkern/OSAtomic.h>
138 156
139 #define SDL_AtomicSet(a, v) \ 157 #define SDL_AtomicCAS(a, oldval, newval) OSAtomicCompareAndSwap32Barrier((oldval), (newval), &(a)->value)
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 158 #if SIZEOF_VOIDP == 4
164 #define SDL_AtomicCASPtr(a, oldval, newval) OSAtomicCompareAndSwap32Barrier((int32_t)(oldval), (int32_t)(newval), (int32_t*)(a)) 159 #define SDL_AtomicCASPtr(a, oldval, newval) OSAtomicCompareAndSwap32Barrier((int32_t)(oldval), (int32_t)(newval), (int32_t*)(a))
165 #elif SIZEOF_VOIDP == 8 160 #elif SIZEOF_VOIDP == 8
166 #define SDL_AtomicCASPtr(a, oldval, newval) OSAtomicCompareAndSwap64Barrier((int64_t)(oldval), (int64_t)(newval), (int64_t*)(a)) 161 #define SDL_AtomicCASPtr(a, oldval, newval) OSAtomicCompareAndSwap64Barrier((int64_t)(oldval), (int64_t)(newval), (int64_t*)(a))
167 #endif 162 #endif
168 163
169 #elif defined(HAVE_GCC_ATOMICS) 164 #elif HAVE_GCC_ATOMICS
170 165
171 #define SDL_AtomicSet(a, v) __sync_lock_test_and_set(&(a)->value, v) 166 #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) 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)
174 #define SDL_AtomicCAS(a, oldval, newval) __sync_bool_compare_and_swap(&(a)->value, oldval, newval) 169 #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) 170 #define SDL_AtomicCASPtr(a, oldval, newval) __sync_bool_compare_and_swap(a, oldval, newval)
178 171
179 #endif 172 #endif
180 173
181 #endif /* !SDL_DISABLE_ATOMIC_INLINE */ 174 #endif /* !SDL_DISABLE_ATOMIC_INLINE */
193 * \brief Set an atomic variable to a value. 186 * \brief Set an atomic variable to a value.
194 * 187 *
195 * \return The previous value of the atomic variable. 188 * \return The previous value of the atomic variable.
196 */ 189 */
197 #ifndef SDL_AtomicSet 190 #ifndef SDL_AtomicSet
198 extern DECLSPEC int SDLCALL SDL_AtomicSet(SDL_atomic_t *a, int value); 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 199 #endif
200 200
201 /** 201 /**
202 * \brief Get the value of an atomic variable 202 * \brief Get the value of an atomic variable
203 */ 203 */
204 #ifndef SDL_AtomicGet 204 #ifndef SDL_AtomicGet
205 extern DECLSPEC int SDLCALL SDL_AtomicGet(SDL_atomic_t *a); 205 #define SDL_AtomicGet(a) \
206 #endif 206 ({ \
207 207 int _value = (a)->value; \
208 /** 208 SDL_CompilerBarrier(); \
209 * \brief Add to an atomic variable. 209 _value; \
210 })
211 #endif
212
213 /**
214 * \brief Add to an atomic variable.
210 * 215 *
211 * \return The previous value of the atomic variable. 216 * \return The previous value of the atomic variable.
217 *
218 * \note This same style can be used for any number operation
212 */ 219 */
213 #ifndef SDL_AtomicAdd 220 #ifndef SDL_AtomicAdd
214 extern DECLSPEC int SDLCALL SDL_AtomicAdd(SDL_atomic_t *a, int value); 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 })
215 #endif 229 #endif
216 230
217 /** 231 /**
218 * \brief Increment an atomic variable used as a reference count. 232 * \brief Increment an atomic variable used as a reference count.
219 */ 233 */
220 #ifndef SDL_AtomicIncRef 234 #ifndef SDL_AtomicIncRef
221 extern DECLSPEC void SDLCALL SDL_AtomicIncRef(SDL_atomic_t *a); 235 #define SDL_AtomicIncRef(a) SDL_AtomicAdd(a, 1)
222 #endif 236 #endif
223 237
224 /** 238 /**
225 * \brief Decrement an atomic variable used as a reference count. 239 * \brief Decrement an atomic variable used as a reference count.
226 * 240 *
227 * \return SDL_TRUE if the variable has reached zero after decrementing, 241 * \return SDL_TRUE if the variable reached zero after decrementing,
228 * SDL_FALSE otherwise 242 * SDL_FALSE otherwise
229 */ 243 */
230 #ifndef SDL_AtomicDecRef 244 #ifndef SDL_AtomicDecRef
231 extern DECLSPEC SDL_bool SDLCALL SDL_AtomicDecRef(SDL_atomic_t *a); 245 #define SDL_AtomicDecRef(a) (SDL_AtomicAdd(a, -1) == 1)
232 #endif 246 #endif
233 247
234 /** 248 /**
235 * \brief Set an atomic variable to a new value if it is currently an old value. 249 * \brief Set an atomic variable to a new value if it is currently an old value.
236 * 250 *
237 * \return SDL_TRUE if the atomic variable was set, SDL_FALSE otherwise. 251 * \return SDL_TRUE if the atomic variable was set, SDL_FALSE otherwise.
238 * 252 *
239 * \note If you don't know what this function is for, you shouldn't use it! 253 * \note If you don't know what this function is for, you shouldn't use it!
240 */ 254 */
241 #ifndef SDL_AtomicCAS 255 #ifndef SDL_AtomicCAS
242 extern DECLSPEC SDL_bool SDLCALL SDL_AtomicCAS(SDL_atomic_t *a, int oldval, int newval); 256 #define SDL_AtomicCAS SDL_AtomicCAS_
243 #endif 257 #endif
258 extern DECLSPEC SDL_bool SDLCALL SDL_AtomicCAS_(SDL_atomic_t *a, int oldval, int newval);
244 259
245 /** 260 /**
246 * \brief Set a pointer to a value atomically. 261 * \brief Set a pointer to a value atomically.
262 *
263 * \return The previous value of the pointer.
247 */ 264 */
248 #ifndef SDL_AtomicSetPtr 265 #ifndef SDL_AtomicSetPtr
249 extern DECLSPEC void SDLCALL SDL_AtomicSetPtr(void** a, void* value); 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 })
250 #endif 274 #endif
251 275
252 /** 276 /**
253 * \brief Get the value of a pointer atomically. 277 * \brief Get the value of a pointer atomically.
254 */ 278 */
255 #ifndef SDL_AtomicGetPtr 279 #ifndef SDL_AtomicGetPtr
256 extern DECLSPEC void* SDLCALL SDL_AtomicGetPtr(void** a); 280 #define SDL_AtomicGetPtr(a) \
281 ({ \
282 void* _value = *(a); \
283 SDL_CompilerBarrier(); \
284 _value; \
285 })
257 #endif 286 #endif
258 287
259 /** 288 /**
260 * \brief Set a pointer to a new value if it is currently an old value. 289 * \brief Set a pointer to a new value if it is currently an old value.
261 * 290 *
262 * \return SDL_TRUE if the pointer was set, SDL_FALSE otherwise. 291 * \return SDL_TRUE if the pointer was set, SDL_FALSE otherwise.
263 * 292 *
264 * \note If you don't know what this function is for, you shouldn't use it! 293 * \note If you don't know what this function is for, you shouldn't use it!
265 */ 294 */
266 #ifndef SDL_AtomicCASPtr 295 #ifndef SDL_AtomicCASPtr
267 extern DECLSPEC SDL_bool SDLCALL SDL_AtomicCASPtr(void **a, void *oldval, void *newval); 296 #define SDL_AtomicCASPtr SDL_AtomicCASPtr_
268 #endif 297 #endif
298 extern DECLSPEC SDL_bool SDLCALL SDL_AtomicCASPtr_(void **a, void *oldval, void *newval);
269 299
270 /* Ends C function definitions when using C++ */ 300 /* Ends C function definitions when using C++ */
271 #ifdef __cplusplus 301 #ifdef __cplusplus
272 /* *INDENT-OFF* */ 302 /* *INDENT-OFF* */
273 } 303 }