Path: blob/main/system/include/SDL/SDL_atomic.h
6169 views
/*1Simple DirectMedia Layer2Copyright (C) 1997-2011 Sam Lantinga <[email protected]>34This software is provided 'as-is', without any express or implied5warranty. In no event will the authors be held liable for any damages6arising from the use of this software.78Permission is granted to anyone to use this software for any purpose,9including commercial applications, and to alter it and redistribute it10freely, subject to the following restrictions:11121. The origin of this software must not be misrepresented; you must not13claim that you wrote the original software. If you use this software14in a product, an acknowledgment in the product documentation would be15appreciated but is not required.162. Altered source versions must be plainly marked as such, and must not be17misrepresented as being the original software.183. This notice may not be removed or altered from any source distribution.19*/2021/**22* \file SDL_atomic.h23*24* Atomic operations.25*26* IMPORTANT:27* If you are not an expert in concurrent lockless programming, you should28* only be using the atomic lock and reference counting functions in this29* file. In all other cases you should be protecting your data structures30* with full mutexes.31*32* The list of "safe" functions to use are:33* SDL_AtomicLock()34* SDL_AtomicUnlock()35* SDL_AtomicIncRef()36* SDL_AtomicDecRef()37*38* Seriously, here be dragons!39* ^^^^^^^^^^^^^^^^^^^^^^^^^^^40*41* You can find out a little more about lockless programming and the42* subtle issues that can arise here:43* http://msdn.microsoft.com/en-us/library/ee418650%28v=vs.85%29.aspx44*45* There's also lots of good information here:46* http://www.1024cores.net/home/lock-free-algorithms47*48* These operations may or may not actually be implemented using49* processor specific atomic operations. When possible they are50* implemented as true processor specific atomic operations. When that51* is not possible the are implemented using locks that *do* use the52* available atomic operations.53*54* All of the atomic operations that modify memory are full memory barriers.55*/5657#ifndef _SDL_atomic_h_58#define _SDL_atomic_h_5960#include "SDL_stdinc.h"61#include "SDL_platform.h"6263#include "begin_code.h"6465/* Need to do this here because intrin.h has C++ code in it */66/* Visual Studio 2005 has a bug where intrin.h conflicts with winnt.h */67#if defined(_MSC_VER) && (_MSC_VER >= 1500) && !defined(_WIN32_WCE)68#include <intrin.h>69#define HAVE_MSC_ATOMICS 170#endif7172/* Set up for C function definitions, even when using C++ */73#ifdef __cplusplus74/* *INDENT-OFF* */75extern "C" {76/* *INDENT-ON* */77#endif7879/**80* \name SDL AtomicLock81*82* The atomic locks are efficient spinlocks using CPU instructions,83* but are vulnerable to starvation and can spin forever if a thread84* holding a lock has been terminated. For this reason you should85* minimize the code executed inside an atomic lock and never do86* expensive things like API or system calls while holding them.87*88* The atomic locks are not safe to lock recursively.89*90* Porting Note:91* The spin lock functions and type are required and can not be92* emulated because they are used in the atomic emulation code.93*/94/*@{*/9596typedef int SDL_SpinLock;9798/**99* \brief Try to lock a spin lock by setting it to a non-zero value.100*101* \param lock Points to the lock.102*103* \return SDL_TRUE if the lock succeeded, SDL_FALSE if the lock is already held.104*/105extern DECLSPEC SDL_bool SDLCALL SDL_AtomicTryLock(SDL_SpinLock *lock);106107/**108* \brief Lock a spin lock by setting it to a non-zero value.109*110* \param lock Points to the lock.111*/112extern DECLSPEC void SDLCALL SDL_AtomicLock(SDL_SpinLock *lock);113114/**115* \brief Unlock a spin lock by setting it to 0. Always returns immediately116*117* \param lock Points to the lock.118*/119extern DECLSPEC void SDLCALL SDL_AtomicUnlock(SDL_SpinLock *lock);120121/*@}*//*SDL AtomicLock*/122123124/**125* The compiler barrier prevents the compiler from reordering126* reads and writes to globally visible variables across the call.127*/128#ifdef _MSC_VER129void _ReadWriteBarrier(void);130#pragma intrinsic(_ReadWriteBarrier)131#define SDL_CompilerBarrier() _ReadWriteBarrier()132#elif defined(__GNUC__)133#define SDL_CompilerBarrier() __asm__ __volatile__ ("" : : : "memory")134#else135#define SDL_CompilerBarrier() \136({ SDL_SpinLock _tmp = 0; SDL_AtomicLock(&_tmp); SDL_AtomicUnlock(&_tmp); })137#endif138139/* Platform specific optimized versions of the atomic functions,140* you can disable these by defining SDL_DISABLE_ATOMIC_INLINE141*/142#if defined(SDL_ATOMIC_DISABLED) && SDL_ATOMIC_DISABLED143#define SDL_DISABLE_ATOMIC_INLINE144#endif145#ifndef SDL_DISABLE_ATOMIC_INLINE146147#ifdef HAVE_MSC_ATOMICS148149#define SDL_AtomicSet(a, v) _InterlockedExchange((long*)&(a)->value, (v))150#define SDL_AtomicAdd(a, v) _InterlockedExchangeAdd((long*)&(a)->value, (v))151#define SDL_AtomicCAS(a, oldval, newval) (_InterlockedCompareExchange((long*)&(a)->value, (newval), (oldval)) == (oldval))152#define SDL_AtomicSetPtr(a, v) _InterlockedExchangePointer((a), (v))153#if _M_IX86154#define SDL_AtomicCASPtr(a, oldval, newval) (_InterlockedCompareExchange((long*)(a), (long)(newval), (long)(oldval)) == (long)(oldval))155#else156#define SDL_AtomicCASPtr(a, oldval, newval) (_InterlockedCompareExchangePointer((a), (newval), (oldval)) == (oldval))157#endif158159#elif defined(__MACOSX__)160#include <libkern/OSAtomic.h>161162#define SDL_AtomicCAS(a, oldval, newval) OSAtomicCompareAndSwap32Barrier((oldval), (newval), &(a)->value)163#if SIZEOF_VOIDP == 4164#define SDL_AtomicCASPtr(a, oldval, newval) OSAtomicCompareAndSwap32Barrier((int32_t)(oldval), (int32_t)(newval), (int32_t*)(a))165#elif SIZEOF_VOIDP == 8166#define SDL_AtomicCASPtr(a, oldval, newval) OSAtomicCompareAndSwap64Barrier((int64_t)(oldval), (int64_t)(newval), (int64_t*)(a))167#endif168169#elif defined(HAVE_GCC_ATOMICS)170171#define SDL_AtomicSet(a, v) __sync_lock_test_and_set(&(a)->value, v)172#define SDL_AtomicAdd(a, v) __sync_fetch_and_add(&(a)->value, v)173#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)175#define SDL_AtomicCASPtr(a, oldval, newval) __sync_bool_compare_and_swap(a, oldval, newval)176177#endif178179#endif /* !SDL_DISABLE_ATOMIC_INLINE */180181182/**183* \brief A type representing an atomic integer value. It is a struct184* so people don't accidentally use numeric operations on it.185*/186#ifndef SDL_atomic_t_defined187typedef struct { int value; } SDL_atomic_t;188#endif189190/**191* \brief Set an atomic variable to a new value if it is currently an old value.192*193* \return SDL_TRUE if the atomic variable was set, SDL_FALSE otherwise.194*195* \note If you don't know what this function is for, you shouldn't use it!196*/197#ifndef SDL_AtomicCAS198#define SDL_AtomicCAS SDL_AtomicCAS_199#endif200extern DECLSPEC SDL_bool SDLCALL SDL_AtomicCAS_(SDL_atomic_t *a, int oldval, int newval);201202/**203* \brief Set an atomic variable to a value.204*205* \return The previous value of the atomic variable.206*/207#ifndef SDL_AtomicSet208static __inline__ int SDL_AtomicSet(SDL_atomic_t *a, int v)209{210int value;211do {212value = a->value;213} while (!SDL_AtomicCAS(a, value, v));214return value;215}216#endif217218/**219* \brief Get the value of an atomic variable220*/221#ifndef SDL_AtomicGet222static __inline__ int SDL_AtomicGet(SDL_atomic_t *a)223{224int value = a->value;225SDL_CompilerBarrier();226return value;227}228#endif229230/**231* \brief Add to an atomic variable.232*233* \return The previous value of the atomic variable.234*235* \note This same style can be used for any number operation236*/237#ifndef SDL_AtomicAdd238static __inline__ int SDL_AtomicAdd(SDL_atomic_t *a, int v)239{240int value;241do {242value = a->value;243} while (!SDL_AtomicCAS(a, value, (value + v)));244return value;245}246#endif247248/**249* \brief Increment an atomic variable used as a reference count.250*/251#ifndef SDL_AtomicIncRef252#define SDL_AtomicIncRef(a) SDL_AtomicAdd(a, 1)253#endif254255/**256* \brief Decrement an atomic variable used as a reference count.257*258* \return SDL_TRUE if the variable reached zero after decrementing,259* SDL_FALSE otherwise260*/261#ifndef SDL_AtomicDecRef262#define SDL_AtomicDecRef(a) (SDL_AtomicAdd(a, -1) == 1)263#endif264265/**266* \brief Set a pointer to a new value if it is currently an old value.267*268* \return SDL_TRUE if the pointer was set, SDL_FALSE otherwise.269*270* \note If you don't know what this function is for, you shouldn't use it!271*/272#ifndef SDL_AtomicCASPtr273#define SDL_AtomicCASPtr SDL_AtomicCASPtr_274#endif275extern DECLSPEC SDL_bool SDLCALL SDL_AtomicCASPtr_(void* *a, void *oldval, void *newval);276277/**278* \brief Set a pointer to a value atomically.279*280* \return The previous value of the pointer.281*/282#ifndef SDL_AtomicSetPtr283static __inline__ void* SDL_AtomicSetPtr(void* *a, void* v)284{285void* value;286do {287value = *a;288} while (!SDL_AtomicCASPtr(a, value, v));289return value;290}291#endif292293/**294* \brief Get the value of a pointer atomically.295*/296#ifndef SDL_AtomicGetPtr297static __inline__ void* SDL_AtomicGetPtr(void* *a)298{299void* value = *a;300SDL_CompilerBarrier();301return value;302}303#endif304305306/* Ends C function definitions when using C++ */307#ifdef __cplusplus308/* *INDENT-OFF* */309}310/* *INDENT-ON* */311#endif312313#include "close_code.h"314315#endif /* _SDL_atomic_h_ */316317/* vi: set ts=4 sw=4 expandtab: */318319320