SDL-mirror/include/SDL_atomic.h

675 lines
25 KiB
C
Raw Normal View History

/*
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 <windows.h>
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: */