/* SDL - Simple DirectMedia Layer Copyright (C) 1997-2006 Sam Lantinga This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Sam Lantinga slouken@libsdl.org */ /** * \file SDL_atomic.h * * Atomic int and pointer magic */ #ifndef _SDL_atomic_h_ #define _SDL_atomic_h_ #include "SDL_stdinc.h" #include "SDL_platform.h" #include "begin_code.h" /* Set up for C function definitions, even when using C++ */ #ifdef __cplusplus /* *INDENT-OFF* */ extern "C" { /* *INDENT-ON* */ #endif #if defined(__GNUC__) && (defined(i386) || defined(__i386__) || defined(__x86_64__)) static __inline__ void SDL_atomic_int_add(volatile int *atomic, int value) { __asm__ __volatile__("lock;" "addl %1, %0":"=m"(*atomic) :"ir"(value), "m"(*atomic)); } static __inline__ int SDL_atomic_int_xchg_add(volatile int *atomic, int value) { int rv; __asm__ __volatile__("lock;" "xaddl %0, %1":"=r"(rv), "=m"(*atomic) :"0"(value), "m"(*atomic)); return rv; } static __inline__ SDL_bool SDL_atomic_int_cmp_xchg(volatile int *atomic, int oldvalue, int newvalue) { int rv; __asm__ __volatile__("lock;" "cmpxchgl %2, %1":"=a"(rv), "=m"(*atomic) :"r"(newvalue), "m"(*atomic), "0"(oldvalue)); return (SDL_bool) (rv == oldvalue); } static __inline__ SDL_bool SDL_atomic_ptr_cmp_xchg(volatile void **atomic, void *oldvalue, void *newvalue) { void *rv; __asm__ __volatile__("lock;" # if defined(__x86_64__) "cmpxchgq %q2, %1" # else "cmpxchgl %2, %1" # endif :"=a"(rv), "=m"(*atomic) :"r"(newvalue), "m"(*atomic), "0"(oldvalue)); return (SDL_bool) (rv == oldvalue); } #elif defined(__GNUC__) && defined(__alpha__) # define ATOMIC_MEMORY_BARRIER (__asm__ __volatile__ ("mb" : : : "memory")) # define ATOMIC_INT_CMP_XCHG(atomic,value) \ ({ \ int rv,prev; \ __asm__ __volatile__(" mb\n" \ "1: ldl_l %0,%2\n" \ " cmpeq %0,%3,%1\n" \ " beq %1,2f\n" \ " mov %4,%1\n" \ " stl_c %1,%2\n" \ " beq %1,1b\n" \ " mb\n" \ "2:" \ : "=&r" (prev), \ "=&r" (rv) \ : "m" (*(atomic)), \ "Ir" (oldvalue), \ "Ir" (newvalue) \ : "memory"); \ (rv != 0); \ }) # if (SIZEOF_VOIDP == 4) static __inline__ SDL_bool SDL_atomic_ptr_cmp_xchg(volatile void **atomic, void *oldvalue, void *newvalue) { int rv; void *prev; __asm__ __volatile__(" mb\n" "1: ldl_l %0,%2\n" " cmpeq %0,%3,%1\n" " beq $1,2f\n" " mov %4,%1\n" " stl_c %1,%2\n" " beq %1,1b\n" " mb\n" "2:":"=&r"(prev), "=&r"(rv) :"m"(*atomic), "Ir"(oldvalue), "Ir"(newvalue) :"memory"); return (SDL_bool) (rv != 0); } # elif (SIZEOF_VOIDP == 8) static __inline__ SDL_bool SDL_atomic_ptr_cmp_xchg(volatile void **atomic, void *oldvalue, void *newvalue) { int rv; void *prev; __asm__ __volatile__(" mb\n" "1: ldq_l %0,%2\n" " cmpeq %0,%3,%1\n" " beq %1,2f\n" " mov %4,%1\n" " stq_c %1,%2\n" " beq %1,1b\n" " mb\n" "2:":"=&r"(prev), "=&r"(rv) :"m"(*atomic), "Ir"(oldvalue), "Ir"(newvalue) :"memory"); return (SDL_bool) (rv != 0); } # else # error "Your system has an unsupported pointer size" # endif /* SIZEOF_VOIDP */ #elif defined(__GNUC__) && defined(__sparc__) # define ATOMIC_MEMORY_BARRIER \ (__asm__ __volatile__("membar #LoadLoad | #LoadStore" \ " | #StoreLoad | #StoreStore" : : : "memory")) # define ATOMIC_INT_CMP_XCHG(atomic,oldvalue,newvalue) \ ({ \ int rv; \ __asm__ __volatile__("cas [%4], %2, %0" \ : "=r" (rv), "=m" (*(atomic)) \ : "r" (oldvalue), "m" (*(atomic)), \ "r" (atomic), "0" (newvalue)); \ rv == oldvalue; \ }) # if (SIZEOF_VOIDP == 4) static __inline__ SDL_bool SDL_atomic_ptr_cmp_xchg(volatile void **atomic, void *oldvalue, void *newvalue) { void *rv; __asm__ __volatile__("cas [%4], %2, %0":"=r"(rv), "=m"(*atomic) :"r"(oldvalue), "m"(*atomic), "r"(atomic), "0"(newvalue)); return (SDL_bool) (rv == oldvalue); } # elif (SIZEOF_VOIDP == 8) static __inline__ SDL_bool SDL_atomic_ptr_cmp_xchg(volatile void **atomic, void *oldvalue, void *newvalue) { void *rv; void **a = atomic; __asm__ __volatile__("casx [%4], %2, %0":"=r"(rv), "=m"(*a) :"r"(oldvalue), "m"(*a), "r"(a), "0"(newvalue)); return (SDL_bool) (rv == oldvalue); } # else # error "Your system has an unsupported pointer size" # endif /* SIZEOF_VOIDP */ #elif defined(__GNUC__) && (defined(__POWERPC__) || defined(__powerpc__) || defined(__ppc__) || defined(_M_PPC)) # define ATOMIC_MEMORY_BARRIER \ (__asm__ __volatile__ ("sync" : : : "memory")) static __inline__ void SDL_atomic_int_add(volatile int *atomic, int value) { int rv, tmp; __asm__ __volatile__("1: lwarx %0, 0, %3\n" " add %1, %0, %4\n" " stwcx. %1, 0, %3\n" " bne- 1b":"=&b"(rv), "=&r"(tmp), "=m"(*atomic) :"b"(atomic), "r"(value), "m"(*atomic) :"cr0", "memory"); } static __inline__ int SDL_atomic_int_xchg_add(volatile int *atomic, int value) { int rv, tmp; __asm__ __volatile__("1: lwarx %0, 0, %3\n" " add %1, %0, %4\n" " stwcx. %1, 0, %3\n" " bne- 1b":"=&b"(rv), "=&r"(tmp), "=m"(*atomic) :"b"(atomic), "r"(value), "m"(*atomic) :"cr0", "memory"); return rv; } # if (SIZEOF_VOIDP == 4) static __inline__ SDL_bool SDL_atomic_int_cmp_xchg(volatile int *atomic, int oldvalue, int newvalue) { int rv; __asm__ __volatile__(" sync\n" "1: lwarx %0, 0, %1\n" " subf. %0, %2, %0\n" " bne 2f\n" " stwcx. %3, 0, %1\n" " bne- 1b\n" "2: isync":"=&r"(rv) :"b"(atomic), "r"(oldvalue), "r":"cr0", "memory"); return (SDL_bool) (rv == 0); } static __inline__ SDL_bool SDL_atomic_ptr_cmp_xchg(volatile void **atomic, void *oldvalue, void *newvalue) { void *rv; __asm__ __volatile__("sync\n" "1: lwarx %0, 0, %1\n" " subf. %0, %2, %0\n" " bne 2f\n" " stwcx. %3, 0, %1\n" " bne- 1b\n" "2: isync":"=&r"(rv) :"b"(atomic), "r"(oldvalue), "r"(newvalue) :"cr0", "memory"); return (SDL_bool) (rv == 0); } # elif (SIZEOF_VOIDP == 8) static __inline__ SDL_bool SDL_atomic_int_cmp_xchg(volatile int *atomic, int oldvalue, int newvalue) { int rv; __asm__ __volatile__(" sync\n" "1: lwarx %0, 0, %1\n" " extsw %0, %0\n" " subf. %0, %2, %0\n" " bne 2f\n" " stwcx. %3, 0, %1\n" " bne- 1b\n" "2: isync":"=&r"(rv) :"b"(atomic), "r"(oldvalue), "r":"cr0", "memory"); return (SDL_bool) (rv == 0); } static __inline__ SDL_bool SDL_atomic_ptr_cmp_xchg(volatile void **atomic, void *oldvalue, void *newvalue) { void *rv; __asm__ __volatile__("sync\n" "1: ldarx %0, 0, %1\n" " subf. %0, %2, %0\n" " bne 2f\n" " stdcx. %3, 0, %1\n" " bne- 1b\n" "2: isync":"=&r"(rv) :"b"(atomic), "r"(oldvalue), "r"(newvalue) :"cr0", "memory"); return (SDL_bool) (rv == 0); } # else # error "Your system has an unsupported pointer size" # endif /* SIZEOF_VOIDP */ #elif defined(__GNUC__) && (defined(__IA64__) || defined(__ia64__)) # define ATOMIC_MEMORY_BARRIER (__sync_synchronize()) # define SDL_atomic_int_xchg_add(atomic, value) \ (__sync_fetch_and_add((atomic),(value))) # define SDL_atomic_int_add(atomic, value) \ ((void)__sync_fetch_and_add((atomic),(value))) # define SDL_atomic_int_cmp_xchg(atomic,oldvalue,newvalue) \ (__sync_bool_compare_and_swap((atomic),(oldvalue),(newvalue))) # define SDL_atomic_ptr_cmp_xchg(atomic,oldvalue,newvalue) \ (__sync_bool_compare_and_swap((long*)(atomic),(long)(oldvalue),(long)(newvalue))) #elif defined(__GNUC__) && defined(__LINUX__) && (defined(__mips__) || defined(__MIPS__)) static __inline__ int SDL_atomic_int_xchg_add(volatile int *atomic, int value) { int rv, tmp; __asm__ __volatile__("1: \n" ".set push \n" ".set mips2 \n" "ll %0,%3 \n" "addu %1,%4,%0 \n" "sc %1,%2 \n" ".set pop \n" "beqz %1,1b \n":"=&r"(rv), "=&r"(tmp), "=m"(*atomic) :"m"(*atomic), "r"(value) :"memory"); return rv; } static __inline__ void SDL_atomic_int_add(volatile int *atomic, int value) { int rv; __asm__ __volatile__("1: \n" ".set push \n" ".set mips2 \n" "ll %0,%2 \n" "addu %0,%3,%0 \n" "sc %0,%1 \n" ".set pop \n" "beqz %0,1b \n":"=&r"(rv), "=m"(*atomic) :"m"(*atomic), "r"(value) :"memory"); } static __inline__ SDL_bool SDL_atomic_int_cmp_xchg(volatile int *atomic, int oldvalue, int newvalue) { int rv; __asm__ __volatile__(" .set push \n" " .set noat \n" " .set mips3 \n" "1: ll %0, %2 \n" " bne %0, %z3, 2f \n" " .set mips0 \n" " move $1, %z4 \n" " .set mips3 \n" " sc $1, %1 \n" " beqz $1, 1b \n" " sync \n" "2: \n" " .set pop \n":"=&r"(rv), "=R"(*atomic) :"R"(*atomic), "Jr"(oldvalue), "Jr"(newvalue) :"memory"); return (SDL_bool) rv; } static __inline__ SDL_bool SDL_atomic_ptr_cmp_xchg(volatile void **atomic, void *oldvalue, void *newvalue) { int rv; __asm__ __volatile__(" .set push \n" " .set noat \n" " .set mips3 \n" # if defined(__mips64) "1: lld %0, %2 \n" # else "1: ll %0, %2 \n" # endif " bne %0, %z3, 2f \n" " move $1, %z4 \n" # if defined(__mips64) " sc $1, %1 \n" # else " scd $1, %1 \n" # endif " beqz $1, 1b \n" " sync \n" "2: \n" " .set pop \n":"=&r"(rv), "=R"(*atomic) :"R"(*atomic), "Jr"(oldvalue), "Jr"(newvalue) :"memory"); return (SDL_bool) rv; } #elif defined(__GNUC__) && defined(__m68k__) static __inline__ int SDL_atomic_int_xchg_add(volatile int *atomic, int value) { int rv = *atomic; int tmp; __asm__ __volatile__("1: move%.l %0,%1 \n" " add%.l %2,%1 \n" " cas%.l %0,%1,%3 \n" " jbne 1b \n":"=d"(rv), "=&d"(tmp) :"d"(value), "m"(*atomic), "0"(rv) :"memory"); return (SDL_bool) rv; } static __inline__ void SDL_atomic_int_add(volatile int *atomic, int value) { __asm__ __volatile__("add%.l %0,%1"::"id"(value), "m"(*atomic) :"memory"); } static __inline__ SDL_bool SDL_atomic_int_cmp_xchg(volatile int *atomic, int oldvalue, int newvalue) { char rv; int readvalue; __asm__ __volatile__("cas%.l %2,%3,%1\n" "seq %0":"=dm"(rv), "=m"(*atomic), "=d"(readvalue) :"d"(newvalue), "m"(*atomic), "2"(oldvalue)); return (SDL_bool) rv; } static __inline__ SDL_bool SDL_atomic_ptr_cmp_xchg(volatile void **atomic, void *oldvalue, void *newvalue) { char rv; int readvalue; __asm__ __volatile__("cas%.l %2,%3,%1\n" "seq %0":"=dm"(rv), "=m"(*atomic), "=d"(readvalue) :"d"(newvalue), "m"(*atomic), "2"(oldvalue)); return (SDL_bool) rv; } #elif defined(__GNUC__) && defined(__s390__) # define ATOMIC_INT_CMP_XCHG(atomic,oldvalue,newvalue) \ ({ \ int rv = oldvalue; \ __asm__ __volatile__("cs %0, %2, %1" \ : "+d" (rv), \ "=Q" (*(atomic)) \ : "d" (newvalue), \ "m" (*(atomic)) \ : "cc"); \ rv == oldvalue; \ }) # if (SIZEOF_VOIDP == 4) static __inline__ SDL_bool SDL_atomic_ptr_cmp_xchg(volatile void **atomic, void *oldvalue, void *newvalue) { void *rv = oldvalue; __asm__ __volatile__("cs %0, %2, %1":"+d"(rv), "=Q"(*atomic) :"d"(newvalue), "m"(*atomic) :"cc"); return (SDL_bool) (rv == oldvalue); } # elif (SIZEOF_VOIDP == 8) static __inline__ SDL_bool SDL_atomic_ptr_cmp_xchg(volatile void **atomic, void *oldvalue, void *newvalue) { void *rv = oldvalue; void **a = atomic; __asm__ __volatile__("csg %0, %2, %1":"+d"(rv), "=Q"(*a) :"d"((long) (newvalue)), "m"(*a) :"cc"); return (SDL_bool) (rv == oldvalue); } # else # error "Your system has an unsupported pointer size" # endif /* SIZEOF_VOIDP */ #elif defined(__WIN32__) # include static __inline__ int SDL_atomic_int_xchg_add(volatile int *atomic, int value) { return InterlockedExchangeAdd(atomic, value); } static __inline__ void SDL_atomic_int_add(volatile int *atomic, int value) { InterlockedExchangeAdd(atomic, value); } # if (WINVER > 0X0400) static __inline__ SDL_bool SDL_atmoic_int_cmp_xchg(volatile int *atomic, int oldvalue, int newvalue) { return (SDL_bool) (InterlockedCompareExchangePointer((PVOID *) atomic, (PVOID) newvalue, (PVOID) oldvalue) == oldvalue); } static __inline__ SDL_bool SDL_atomic_ptr_cmp_xchg(volatile void **atomic, void *oldvalue, void *newvalue) { return (InterlockedCompareExchangePointer(atomic, newvalue, oldvalue) == oldvalue); } # else /* WINVER <= 0x0400 */ # if (SIZEOF_VOIDP != 4) # error "InterlockedCompareExchangePointer needed" # endif static __inline__ SDL_bool SDL_atomic_int_cmp_xchg(volatile int *atomic, int oldvalue, int newvalue) { return (InterlockedCompareExchange(atomic, newvalue, oldvalue) == oldvalue); } static __inline__ SDL_bool SDL_atomic_ptr_cmp_xchg(volatile void **atomic, void *oldvalue, void *newvalue) { return (InterlockedCompareExchange(atomic, newvalue, oldvalue) == oldvalue); } # endif #else /* when all else fails */ # define SDL_ATOMIC_OPS_NOT_SUPPORTED # warning "Atomic Ops for this platform not supported!" static __inline__ int SDL_atomic_int_xchg_add(volatile int *atomic, int value) { int rv = *atomic; *(atomic) += value; return rv; } static __inline__ SDL_bool SDL_atomic_int_cmp_xchg(volatile int *atomic, int oldvalue, int newvalue) { return (*atomic == oldvalue) ? ((*atomic = newvalue), SDL_TRUE) : SDL_FALSE; } static __inline__ void SDL_atomic_int_add(volatile int *atomic, int value) { *atomic += value; } #endif /* arch & platforms */ #ifdef ATOMIC_INT_CMP_XCHG static __inline__ SDL_bool SDL_atomic_int_cmp_xchg(volatile int *atomic, int oldvalue, int newvalue) { return (SDL_bool) ATOMIC_INT_CMP_XCHG(atomic, oldvalue, newvalue); } static __inline__ int SDL_atomic_int_xchg_add(volatile int *atomic, int value) { int rv; do rv = *atomic; while (!ATOMIC_INT_CMP_XCHG(atomic, rv, rv + value)); return rv; } static __inline__ void SDL_atomic_int_add(volatile int *atomic, int value) { int rv; do rv = *atomic; while (!ATOMIC_INT_CMP_XCHG(atomic, rv, rv + value)); } #endif /* ATOMIC_CMP_XCHG */ #ifdef ATOMIC_MEMORY_BARRIER # define SDL_atomic_int_get(atomic) \ (ATOMIC_MEMORY_BARRIER,*(atomic)) # define SDL_atomic_int_set(atomic,value) \ (*(atomic)=value,ATOMIC_MEMORY_BARRIER) #else # define SDL_atomic_int_get(atomic) (*(atomic)) # define SDL_atomic_int_set(atomic, newvalue) ((void)(*(atomic) = (newvalue))) #endif /* MEMORY_BARRIER_NEEDED */ #define SDL_atomic_int_inc(atomic) (SDL_atomic_int_add((atomic),1)) #define SDL_atomic_int_dec_test(atomic) (SDL_atomic_int_xchg_add((atomic),-1) == 1) /* Ends C function definitions when using C++ */ #ifdef __cplusplus /* *INDENT-OFF* */ } /* *INDENT-ON* */ #endif #include "close_code.h" #endif /* _SDL_atomic_h_ */ /* vi: set ts=4 sw=4 expandtab: */