1
1
openmpi/opal/include/sys/atomic.h
Jeff Squyres 42ec26e640 Update the copyright notices for IU and UTK.
This commit was SVN r7999.
2005-11-05 19:57:48 +00:00

532 строки
18 KiB
C

/*
* Copyright (c) 2004-2005 The Trustees of Indiana University and Indiana
* University Research and Technology
* Corporation. All rights reserved.
* Copyright (c) 2004-2005 The University of Tennessee and The University
* of Tennessee Research Foundation. All rights
* reserved.
* Copyright (c) 2004-2005 High Performance Computing Center Stuttgart,
* University of Stuttgart. All rights reserved.
* Copyright (c) 2004-2005 The Regents of the University of California.
* All rights reserved.
* $COPYRIGHT$
*
* Additional copyrights may follow
*
* $HEADER$
*/
/** @file
*
* Atomic operations.
*
* This API is patterned after the FreeBSD kernel atomic interface
* (which is influenced by Intel's ia64 architecture). The
* FreeBSD interface is documented at
*
* http://www.freebsd.org/cgi/man.cgi?query=atomic&sektion=9
*
* Only the necessary subset of functions are implemented here.
*
* The following #defines will be true / false based on
* assembly support:
*
* - \c OPAL_HAVE_ATOMIC_MEM_BARRIER atomic memory barriers
* - \c OPAL_HAVE_ATOMIC_SPINLOCKS atomic spinlocks
* - \c OPAL_HAVE_ATOMIC_MATH_32 if 32 bit add/sub/cmpset can be done "atomicly"
* - \c OPAL_HAVE_ATOMIC_MATH_64 if 32 bit add/sub/cmpset can be done "atomicly"
*
* Note that for the Atomic math, atomic add/sub may be implemented as
* C code using opal_atomic_cmpset. The appearance of atomic
* operation will be upheld in these cases.
*/
#ifndef OPAL_SYS_ATOMIC_H
#define OPAL_SYS_ATOMIC_H 1
#include "ompi_config.h"
#include "include/sys/architecture.h"
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
/* do some quick #define cleanup in cases where we are doing
testing... */
#ifdef OMPI_DISABLE_INLINE_ASM
#undef OMPI_C_GCC_INLINE_ASSEMBLY
#define OMPI_C_GCC_INLINE_ASSEMBLY 0
#undef OMPI_CXX_GCC_INLINE_ASSEMBLY
#define OMPI_CXX_GCC_INLINE_ASSEMBLY 0
#undef OMPI_C_DEC_INLINE_ASSEMBLY
#define OMPI_C_DEC_INLINE_ASSEMBLY 0
#undef OMPI_CXX_DEC_INLINE_ASSEMBLY
#define OMPI_CXX_DEC_INLINE_ASSEMBLY 0
#undef OMPI_C_XLC_INLINE_ASSEMBLY
#define OMPI_C_XLC_INLINE_ASSEMBLY 0
#undef OMPI_CXX_XLC_INLINE_ASSEMBLY
#define OMPI_CXX_XLC_INLINE_ASSEMBLY 0
#endif
/* define OMPI_{GCC,DEC,XLC}_INLINE_ASSEMBLY based on the
OMPI_{C,CXX}_{GCC,DEC,XLC}_INLINE_ASSEMBLY defines and whether we
are in C or C++ */
#if defined(c_plusplus) || defined(__cplusplus)
#define OMPI_GCC_INLINE_ASSEMBLY OMPI_CXX_GCC_INLINE_ASSEMBLY
#define OMPI_DEC_INLINE_ASSEMBLY OMPI_CXX_DEC_INLINE_ASSEMBLY
#define OMPI_XLC_INLINE_ASSEMBLY OMPI_CXX_XLC_INLINE_ASSEMBLY
#else
#define OMPI_GCC_INLINE_ASSEMBLY OMPI_C_GCC_INLINE_ASSEMBLY
#define OMPI_DEC_INLINE_ASSEMBLY OMPI_C_DEC_INLINE_ASSEMBLY
#define OMPI_XLC_INLINE_ASSEMBLY OMPI_C_XLC_INLINE_ASSEMBLY
#endif
#if defined(c_plusplus) || defined(__cplusplus)
extern "C" {
#endif
/**********************************************************************
*
* Data structures for atomic ops
*
*********************************************************************/
/**
* Volatile lock object (with optional padding).
*
* \note The internals of the lock are included here, but should be
* considered private. The implementation currently in use may choose
* to use an int or unsigned char as the lock value - the user is not
* informed either way.
*/
struct opal_atomic_lock_t {
union {
volatile int lock; /**< The lock address (an integer) */
volatile unsigned char sparc_lock; /**< The lock address on sparc */
char padding[sizeof(int)]; /**< Array for optional padding */
} u;
};
typedef struct opal_atomic_lock_t opal_atomic_lock_t;
/**********************************************************************
*
* Load the appropriate architecture files and set some reasonable
* default values for our support
*
*********************************************************************/
#if defined(DOXYGEN)
/* don't include system-level gorp when generating doxygen files */
#elif OMPI_ASSEMBLY_ARCH == OMPI_WINDOWS || defined(WIN32)
/* windows first, as they have API-level primitives for this stuff */
#include "include/sys/win32/atomic.h"
#elif OMPI_ASSEMBLY_ARCH == OMPI_ALPHA
#include "include/sys/alpha/atomic.h"
#elif OMPI_ASSEMBLY_ARCH == OMPI_AMD64
#include "include/sys/amd64/atomic.h"
#elif OMPI_ASSEMBLY_ARCH == OMPI_IA32
#include "include/sys/ia32/atomic.h"
#elif OMPI_ASSEMBLY_ARCH == OMPI_IA64
#include "include/sys/ia64/atomic.h"
#elif OMPI_ASSEMBLY_ARCH == OMPI_MIPS
#include "include/sys/mips/atomic.h"
#elif OMPI_ASSEMBLY_ARCH == OMPI_POWERPC32
#include "include/sys/powerpc/atomic.h"
#elif OMPI_ASSEMBLY_ARCH == OMPI_POWERPC64
#include "include/sys/powerpc/atomic.h"
#elif OMPI_ASSEMBLY_ARCH == OMPI_SPARC
#include "include/sys/sparc/atomic.h"
#elif OMPI_ASSEMBLY_ARCH == OMPI_SPARCV9_32
#include "include/sys/sparcv9/atomic.h"
#elif OMPI_ASSEMBLY_ARCH == OMPI_SPARCV9_64
#include "include/sys/sparcv9/atomic.h"
#endif
#ifndef DOXYGEN
/* compare and set operations can't really be emulated from software,
so if these defines aren't already set, they should be set to 0
now */
#ifndef OPAL_HAVE_ATOMIC_CMPSET_32
#define OPAL_HAVE_ATOMIC_CMPSET_32 0
#endif
#ifndef OPAL_HAVE_ATOMIC_CMPSET_64
#define OPAL_HAVE_ATOMIC_CMPSET_64 0
#endif
#endif /* DOXYGEN */
/**********************************************************************
*
* Memory Barriers - defined here if running doxygen or have barriers
* but can't inline
*
*********************************************************************/
#if !defined(OPAL_HAVE_ATOMIC_MEM_BARRIER) && !defined(DOXYGEN)
/* no way to emulate in C code */
#define OPAL_HAVE_ATOMIC_MEM_BARRIER 0
#endif
#if defined(DOXYGEN) || OPAL_HAVE_ATOMIC_MEM_BARRIER
/**
* Memory barrier
*
* Will use system-specific features to instruct the processor and
* memory controller that all writes and reads that have been posted
* before the call to \c opal_atomic_mb() must appear to have
* completed before the next read or write.
*
* \note This can have some expensive side effects, including flushing
* the pipeline, preventing the cpu from reordering instructions, and
* generally grinding the memory controller's performance. Use only
* if you need *both* read and write barriers.
*/
void opal_atomic_mb(void);
/**
* Read memory barrier
*
* Use system-specific features to instruct the processor and memory
* conrtoller that all reads that have been posted before the call to
* \c opal_atomic_rmb() must appear to have been completed before the
* next read. Nothing is said about the ordering of writes when using
* \c opal_atomic_rmb().
*/
void opal_atomic_rmb(void);
/**
* Write memory barrier.
*
* Use system-specific features to instruct the processor and memory
* conrtoller that all writes that have been posted before the call to
* \c opal_atomic_wmb() must appear to have been completed before the
* next write. Nothing is said about the ordering of reads when using
* \c opal_atomic_wmb().
*/
void opal_atomic_wmb(void);
#endif /* defined(DOXYGEN) || OPAL_HAVE_ATOMIC_MEM_BARRIER */
/**********************************************************************
*
* Atomic spinlocks - always inlined, if have atomic cmpset
*
*********************************************************************/
#if !defined(OPAL_HAVE_ATOMIC_SPINLOCKS) && !defined(DOXYGEN)
/* 0 is more like "pending" - we'll fix up at the end after all
the static inline functions are declared */
#define OPAL_HAVE_ATOMIC_SPINLOCKS 0
#endif
#if defined(DOXYGEN) || OPAL_HAVE_ATOMIC_SPINLOCKS || (OPAL_HAVE_ATOMIC_CMPSET_32 || OPAL_HAVE_ATOMIC_CMPSET_64)
/**
* Enumeration of lock states
*/
enum {
OPAL_ATOMIC_UNLOCKED = 0,
OPAL_ATOMIC_LOCKED = 1
};
/**
* Initialize a lock to value
*
* @param lock Address of the lock
* @param value Initial value to set lock to
*/
#if OPAL_HAVE_ATOMIC_SPINLOCKS == 0
static inline
#endif
void opal_atomic_init(opal_atomic_lock_t* lock, int value);
/**
* Try to acquire a lock.
*
* @param lock Address of the lock.
* @return 0 if the lock was acquired, 1 otherwise.
*/
#if OPAL_HAVE_ATOMIC_SPINLOCKS == 0
static inline
#endif
int opal_atomic_trylock(opal_atomic_lock_t *lock);
/**
* Acquire a lock by spinning.
*
* @param lock Address of the lock.
*/
#if OPAL_HAVE_ATOMIC_SPINLOCKS == 0
static inline
#endif
void opal_atomic_lock(opal_atomic_lock_t *lock);
/**
* Release a lock.
*
* @param lock Address of the lock.
*/
#if OPAL_HAVE_ATOMIC_SPINLOCKS == 0
static inline
#endif
void opal_atomic_unlock(opal_atomic_lock_t *lock);
#if OPAL_HAVE_ATOMIC_SPINLOCKS == 0
#undef OPAL_HAVE_ATOMIC_SPINLOCKS
#define OPAL_HAVE_ATOMIC_SPINLOCKS (OPAL_HAVE_ATOMIC_CMPSET_32 || OPAL_HAVE_ATOMIC_CMPSET_64)
#define OPAL_NEED_INLINE_ATOMIC_SPINLOCKS
#endif
#endif /* OPAL_HAVE_ATOMIC_SPINLOCKS */
/**********************************************************************
*
* Atomic math operations
*
*********************************************************************/
#if !defined(OPAL_HAVE_ATOMIC_CMPSET_32) && !defined(DOXYGEN)
#define OPAL_HAVE_ATOMIC_CMPSET_32 0
#endif
#if defined(DOXYGEN) || OPAL_HAVE_ATOMIC_CMPSET_32
int opal_atomic_cmpset_32(volatile int32_t *addr, int32_t oldval,
int32_t newval);
int opal_atomic_cmpset_acq_32(volatile int32_t *addr, int32_t oldval,
int32_t newval);
int opal_atomic_cmpset_rel_32(volatile int32_t *addr, int32_t oldval,
int32_t newval);
#endif
#if !defined(OPAL_HAVE_ATOMIC_CMPSET_64) && !defined(DOXYGEN)
#define OPAL_HAVE_ATOMIC_CMPSET_64 0
#endif
#if defined(DOXYGEN) || OPAL_HAVE_ATOMIC_CMPSET_64
int opal_atomic_cmpset_64(volatile int64_t *addr, int64_t oldval,
int64_t newval);
int opal_atomic_cmpset_acq_64(volatile int64_t *addr, int64_t oldval,
int64_t newval);
int opal_atomic_cmpset_rel_64(volatile int64_t *addr, int64_t oldval,
int64_t newval);
#endif
#if !defined(OPAL_HAVE_ATOMIC_MATH_32) && !defined(DOXYGEN)
/* define to 0 for these tests. WIll fix up later. */
#define OPAL_HAVE_ATOMIC_MATH_32 0
#endif
#if defined(DOXYGEN) || OPAL_HAVE_ATOMIC_MATH_32 || OPAL_HAVE_ATOMIC_CMPSET_32
#if ! OPAL_HAVE_ATOMIC_MATH_32
static inline
#endif
int32_t opal_atomic_add_32(volatile int32_t *addr, int delta);
#if ! OPAL_HAVE_ATOMIC_MATH_32
static inline
#endif
int32_t opal_atomic_sub_32(volatile int32_t *addr, int delta);
#endif /* OPAL_HAVE_ATOMIC_MATH_32 */
#if ! OPAL_HAVE_ATOMIC_MATH_32
/* fix up the value of ompi_have_atomic_math_32 to allow for C versions */
#undef OPAL_HAVE_ATOMIC_MATH_32
#define OPAL_HAVE_ATOMIC_MATH_32 OPAL_HAVE_ATOMIC_CMPSET_32
#endif
#ifndef OPAL_HAVE_ATOMIC_MATH_64
/* define to 0 for these tests. WIll fix up later. */
#define OPAL_HAVE_ATOMIC_MATH_64 0
#endif
#if defined(DOXYGEN) || OPAL_HAVE_ATOMIC_MATH_64 || OPAL_HAVE_ATOMIC_CMPSET_64
#if OPAL_HAVE_ATOMIC_CMPSET_64
static inline
#endif
int64_t opal_atomic_add_64(volatile int64_t *addr, int64_t delta);
#if OPAL_HAVE_ATOMIC_CMPSET_64
static inline
#endif
int64_t opal_atomic_sub_64(volatile int64_t *addr, int64_t delta);
#endif /* OPAL_HAVE_ATOMIC_MATH_32 */
#if ! OPAL_HAVE_ATOMIC_MATH_64
/* fix up the value of ompi_have_atomic_math_64 to allow for C versions */
#undef OPAL_HAVE_ATOMIC_MATH_64
#define OPAL_HAVE_ATOMIC_MATH_64 OPAL_HAVE_ATOMIC_CMPSET_64
#endif
/* provide a size_t add/subtract. When in debug mode, make it an
* inline function so that we don't have any casts in the
* interface and can catch type errors. When not in debug mode,
* just make it a macro, so that there's no performance penalty
*/
#if defined(DOXYGEN) || OMPI_ENABLE_DEBUG
static inline size_t
opal_atomic_add_size_t(volatile size_t *addr, int delta)
{
#if SIZEOF_SIZE_T == 4
return (size_t) opal_atomic_add_32((int32_t*) addr, delta);
#elif SIZEOF_SIZE_T == 8
return (size_t) opal_atomic_add_64((int64_t*) addr, delta);
#else
#error "Unknown size_t size"
#endif
}
static inline size_t
opal_atomic_sub_size_t(volatile size_t *addr, int delta)
{
#if SIZEOF_SIZE_T == 4
return (size_t) opal_atomic_sub_32((int32_t*) addr, delta);
#elif SIZEOF_SIZE_T == 8
return (size_t) opal_atomic_sub_64((int64_t*) addr, delta);
#else
#error "Unknown size_t size"
#endif
}
#else
#if SIZEOF_SIZE_T == 4
#define opal_atomic_add_size_t(addr, delta) ((size_t) opal_atomic_add_32((int32_t*) addr, delta))
#define opal_atomic_sub_size_t(addr, delta) ((size_t) opal_atomic_sub_32((int32_t*) addr, delta))
#elif SIZEOF_SIZE_T ==8
#define opal_atomic_add_size_t(addr, delta) ((size_t) opal_atomic_add_64((int64_t*) addr, delta))
#define opal_atomic_sub_size_t(addr, delta) ((size_t) opal_atomic_sub_64((int64_t*) addr, delta))
#else
#error "Unknown size_t size"
#endif
#endif
#if defined(DOXYGEN) || (OPAL_HAVE_ATOMIC_CMPSET_32 || OPAL_HAVE_ATOMIC_CMPSET_64)
/* these are always done with inline functions, so always mark as
static inline */
static inline int opal_atomic_cmpset_xx(volatile void* addr, int64_t oldval,
int64_t newval, size_t length);
static inline int opal_atomic_cmpset_acq_xx(volatile void* addr,
int64_t oldval, int64_t newval,
size_t length);
static inline int opal_atomic_cmpset_rel_xx(volatile void* addr,
int64_t oldval, int64_t newval,
size_t length);
static inline int opal_atomic_cmpset_ptr(volatile void* addr,
void* oldval,
void* newval);
static inline int opal_atomic_cmpset_acq_ptr(volatile void* addr,
void* oldval,
void* newval);
static inline int opal_atomic_cmpset_rel_ptr(volatile void* addr,
void* oldval,
void* newval);
/**
* Atomic compare and set of pointer with relaxed semantics. This
* macro detect at compile time the type of the first argument and
* choose the correct function to be called.
*
* \note This macro should only be used for integer types.
*
* @param addr Address of <TYPE>.
* @param oldval Comparison value <TYPE>.
* @param newval New value to set if comparision is true <TYPE>.
*
* See opal_atomic_cmpset_* for pseudo-code.
*/
#define opal_atomic_cmpset( ADDR, OLDVAL, NEWVAL ) \
opal_atomic_cmpset_xx( (volatile void*)(ADDR), (int64_t)(OLDVAL), \
(int64_t)(NEWVAL), sizeof(*(ADDR)) )
/**
* Atomic compare and set of pointer with acquire semantics. This
* macro detect at compile time the type of the first argument
* and choose the correct function to be called.
*
* \note This macro should only be used for integer types.
*
* @param addr Address of <TYPE>.
* @param oldval Comparison value <TYPE>.
* @param newval New value to set if comparision is true <TYPE>.
*
* See opal_atomic_cmpset_acq_* for pseudo-code.
*/
#define opal_atomic_cmpset_acq( ADDR, OLDVAL, NEWVAL ) \
opal_atomic_cmpset_acq_xx( (volatile void*)(ADDR), (int64_t)(OLDVAL), \
(int64_t)(NEWVAL), sizeof(*(ADDR)) )
/**
* Atomic compare and set of pointer with release semantics. This
* macro detect at compile time the type of the first argument
* and choose the correct function to b
*
* \note This macro should only be used for integer types.
*
* @param addr Address of <TYPE>.
* @param oldval Comparison value <TYPE>.
* @param newval New value to set if comparision is true <TYPE>.
*
* See opal_atomic_cmpsetrel_* for pseudo-code.
*/
#define opal_atomic_cmpset_rel( ADDR, OLDVAL, NEWVAL ) \
opal_atomic_cmpset_rel_xx( (volatile void*)(ADDR), (int64_t)(OLDVAL), \
(int64_t)(NEWVAL), sizeof(*(ADDR)) )
#endif /* (OPAL_HAVE_ATOMIC_CMPSET_32 || OPAL_HAVE_ATOMIC_CMPSET_64) */
#if defined(DOXYGEN) || (OPAL_HAVE_ATOMIC_MATH_32 || OPAL_HAVE_ATOMIC_MATH_64)
static inline void opal_atomic_add_xx(volatile void* addr,
int32_t value, size_t length);
static inline void opal_atomic_sub_xx(volatile void* addr,
int32_t value, size_t length);
static inline int opal_atomic_add_pt(volatile void* addr,
void* delta);
static inline int opal_atomic_sub_ptr(volatile void* addr,
void* delta);
/**
* Atomically increment the content depending on the type. This
* macro detect at compile time the type of the first argument
* and choose the correct function to be called.
*
* \note This macro should only be used for integer types.
*
* @param addr Address of <TYPE>
* @param delta Value to add (converted to <TYPE>).
*/
#define opal_atomic_add( ADDR, VALUE ) \
opal_atomic_add_xx( (volatile void*)(ADDR), (int32_t)(VALUE), \
sizeof(*(ADDR)) )
/**
* Atomically decrement the content depending on the type. This
* macro detect at compile time the type of the first argument
* and choose the correct function to be called.
*
* \note This macro should only be used for integer types.
*
* @param addr Address of <TYPE>
* @param delta Value to substract (converted to <TYPE>).
*/
#define opal_atomic_sub( ADDR, VALUE ) \
opal_atomic_sub_xx( (volatile void*)(ADDR), (int32_t)(VALUE), \
sizeof(*(ADDR)) )
#endif /* OPAL_HAVE_ATOMIC_MATH_32 || OPAL_HAVE_ATOMIC_MATH_64 */
/**********************************************************************
*
* Include system specific inline asm definitions. Otherwise
* the definitions are in system specific .s files in src/util.
*
*********************************************************************/
#include "include/sys/atomic_impl.h"
#if defined(c_plusplus) || defined(__cplusplus)
}
#endif
#endif /* OPAL_SYS_ATOMIC_H */