include/SDL_atomic.h
author Sam Lantinga <slouken@libsdl.org>
Sun, 16 Jan 2011 15:16:39 -0800
changeset 5004 0c72ae7b7cb2
parent 5003 3a95a2b93eb3
child 5005 7f0265279b68
permissions -rw-r--r--
Added native atomic operations for Windows, Mac OS X, and gcc compiler intrinsics.
Changed the CAS return value to bool, so it's efficient with OSAtomicCompareAndSwap32Barrier()
Added an atomic test adapted from code by Michael Davidsaver
     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 #if defined(__WIN32__)
   112 #define WIN32_LEAN_AND_MEAN
   113 #include <windows.h>
   114 
   115 #define SDL_AtomicSet(a, v)     InterlockedExchange(&(a)->value, v)
   116 #define SDL_AtomicGet(a)        ((a)->value)
   117 #define SDL_AtomicAdd(a, v)     InterlockedAdd(&(a)->value, v)
   118 #define SDL_AtomicCAS(a, oldval, newval) (InterlockedCompareExchange(&(a)->value, newval, oldval) == (oldval))
   119 #define SDL_AtomicSetPtr(a, v)  InterlockedExchangePointer(a, v)
   120 #define SDL_AtomicGetPtr(a)     (*(a))
   121 #define SDL_AtomicCASPtr(a, oldval, newval) (InterlockedCompareExchangePointer(a, newval, oldval) == (oldval))
   122 
   123 #elif defined(__MACOSX__)
   124 #include <libkern/OSAtomic.h>
   125 
   126 #define SDL_AtomicSet(a, v) \
   127 ({                          \
   128     int oldvalue;           \
   129                             \
   130     do {                    \
   131         oldvalue = (a)->value; \
   132     } while (!OSAtomicCompareAndSwap32Barrier(oldvalue, v, &(a)->value)); \
   133                             \
   134     oldvalue;               \
   135 })
   136 #define SDL_AtomicGet(a)        ((a)->value)
   137 #define SDL_AtomicAdd(a, v) \
   138 ({                          \
   139     int oldvalue;           \
   140                             \
   141     do {                    \
   142         oldvalue = (a)->value; \
   143     } while (!OSAtomicCompareAndSwap32Barrier(oldvalue, oldvalue+v, &(a)->value)); \
   144                             \
   145     oldvalue;               \
   146 })
   147 #define SDL_AtomicCAS(a, oldval, newval) OSAtomicCompareAndSwap32Barrier(oldval, newval, &(a)->value)
   148 #define SDL_AtomicSetPtr(a, v)  (*(a) = v, OSMemoryBarrier())
   149 #define SDL_AtomicGetPtr(a)     (*(a))
   150 #if SIZEOF_VOIDP == 4
   151 #define SDL_AtomicCASPtr(a, oldval, newval) OSAtomicCompareAndSwap32Barrier((int32_t)(oldval), (int32_t)(newval), (int32_t*)(a))
   152 #elif SIZEOF_VOIDP == 8
   153 #define SDL_AtomicCASPtr(a, oldval, newval) OSAtomicCompareAndSwap64Barrier((int64_t)(oldval), (int64_t)(newval), (int64_t*)(a))
   154 #endif
   155 
   156 #elif defined(HAVE_GCC_ATOMICS)
   157 
   158 #define SDL_AtomicSet(a, v)     __sync_lock_test_and_set(&(a)->value, v)
   159 #define SDL_AtomicGet(a)        ((a)->value)
   160 #define SDL_AtomicAdd(a, v)     __sync_fetch_and_add(&(a)->value, v)
   161 #define SDL_AtomicCAS(a, oldval, newval) __sync_bool_compare_and_swap(&(a)->value, oldval, newval)
   162 #define SDL_AtomicSetPtr(a, v)  (*(a) = v, __sync_synchronize())
   163 #define SDL_AtomicGetPtr(a)     (*(a))
   164 #define SDL_AtomicCASPtr(a, oldval, newval) __sync_bool_compare_and_swap(a, oldval, newval)
   165 
   166 #endif
   167 
   168 /**
   169  * \brief A type representing an atomic integer value.  It is a struct
   170  *        so people don't accidentally use numeric operations on it.
   171  */
   172 #ifndef SDL_atomic_t_defined
   173 typedef struct { int value; } SDL_atomic_t;
   174 #endif
   175 
   176 /**
   177  * \brief Set an atomic variable to a value.
   178  *
   179  * \return The previous value of the atomic variable.
   180  */
   181 #ifndef SDL_AtomicSet
   182 extern DECLSPEC int SDLCALL SDL_AtomicSet(SDL_atomic_t *a, int value);
   183 #endif
   184 
   185 /**
   186  * \brief Get the value of an atomic variable
   187  */
   188 #ifndef SDL_AtomicGet
   189 extern DECLSPEC int SDLCALL SDL_AtomicGet(SDL_atomic_t *a);
   190 #endif
   191 
   192 /**
   193  * \brief  Add to an atomic variable.
   194  *
   195  * \return The previous value of the atomic variable.
   196  */
   197 #ifndef SDL_AtomicAdd
   198 extern DECLSPEC int SDLCALL SDL_AtomicAdd(SDL_atomic_t *a, int value);
   199 #endif
   200 
   201 /**
   202  * \brief Increment an atomic variable used as a reference count.
   203  */
   204 #ifndef SDL_AtomicIncRef
   205 extern DECLSPEC void SDLCALL SDL_AtomicIncRef(SDL_atomic_t *a);
   206 #endif
   207 
   208 /**
   209  * \brief Decrement an atomic variable used as a reference count.
   210  *
   211  * \return SDL_TRUE if the variable has reached zero after decrementing,
   212  *         SDL_FALSE otherwise
   213  */
   214 #ifndef SDL_AtomicDecRef
   215 extern DECLSPEC SDL_bool SDLCALL SDL_AtomicDecRef(SDL_atomic_t *a);
   216 #endif
   217 
   218 /**
   219  * \brief Set an atomic variable to a new value if it is currently an old value.
   220  *
   221  * \return SDL_TRUE if the atomic variable was set, SDL_FALSE otherwise.
   222  *
   223  * \note If you don't know what this function is for, you shouldn't use it!
   224 */
   225 #ifndef SDL_AtomicCAS
   226 extern DECLSPEC SDL_bool SDLCALL SDL_AtomicCAS(SDL_atomic_t *a, int oldval, int newval);
   227 #endif
   228 
   229 /**
   230  * \brief Set a pointer to a value atomically.
   231  */
   232 #ifndef SDL_AtomicSetPtr
   233 extern DECLSPEC void SDLCALL SDL_AtomicSetPtr(void** a, void* value);
   234 #endif
   235 
   236 /**
   237  * \brief Get the value of a pointer atomically.
   238  */
   239 #ifndef SDL_AtomicGetPtr
   240 extern DECLSPEC void* SDLCALL SDL_AtomicGetPtr(void** a);
   241 #endif
   242 
   243 /**
   244  * \brief Set a pointer to a new value if it is currently an old value.
   245  *
   246  * \return SDL_TRUE if the pointer was set, SDL_FALSE otherwise.
   247  *
   248  * \note If you don't know what this function is for, you shouldn't use it!
   249 */
   250 #ifndef SDL_AtomicCASPtr
   251 extern DECLSPEC SDL_bool SDLCALL SDL_AtomicCASPtr(void **a, void *oldval, void *newval);
   252 #endif
   253 
   254 /* Ends C function definitions when using C++ */
   255 #ifdef __cplusplus
   256 /* *INDENT-OFF* */
   257 }
   258 /* *INDENT-ON* */
   259 #endif
   260 
   261 #include "close_code.h"
   262 
   263 #endif /* _SDL_atomic_h_ */
   264 
   265 /* vi: set ts=4 sw=4 expandtab: */