/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil ; -*- */
/*
* (C) 2008 by Argonne National Laboratory.
* See COPYRIGHT in top-level directory.
*/
#ifndef OPA_PRIMITIVES_H_INCLUDED
#define OPA_PRIMITIVES_H_INCLUDED
#include "opa_config.h"
#include "opa_util.h"
/* Clean up some of the opa_config.h definitions. This is a consequence
of using the AX_PREFIX_CONFIG_H macro. Autoconf won't define inline
or _opa_inline when a real "inline" works. Since we are
unconditionally using _opa_inline we must define it ourselves in this
case. */
#ifndef _opa_inline
#define _opa_inline inline
#endif
#ifndef _opa_restrict
#define _opa_restrict restrict
#endif
#ifndef _opa_const
#define _opa_const const
#endif
/*
Primitive atomic functions
--------------------------
The included file is responsible for defining the types of OPA_int_t and
OPA_ptr_t as well as a set of functions for operating on these
types. If you have the following declaration:
OPA_int_t atomic_var;
Then in order for the emulation functions to compile, the underlying value of
atomic_var should be accessible via:
atomic_var.v;
The same goes for OPA_ptr_t.
The atomic functions that must be ported for each architecture:
static _opa_inline int OPA_load_int(_opa_const OPA_int_t *ptr);
static _opa_inline void OPA_store_int(OPA_int_t *ptr, int val);
static _opa_inline void *OPA_load_ptr(_opa_const OPA_ptr_t *ptr);
static _opa_inline void OPA_store_ptr(OPA_ptr_t *ptr, void *val);
static _opa_inline void OPA_add_int(OPA_int_t *ptr, int val);
static _opa_inline void OPA_incr_int(OPA_int_t *ptr);
static _opa_inline void OPA_decr_int(OPA_int_t *ptr);
static _opa_inline int OPA_decr_and_test_int(OPA_int_t *ptr);
static _opa_inline int OPA_fetch_and_add_int(OPA_int_t *ptr, int val);
static _opa_inline int OPA_fetch_and_incr_int(OPA_int_t *ptr);
static _opa_inline int OPA_fetch_and_decr_int(OPA_int_t *ptr);
static _opa_inline void *OPA_cas_ptr(OPA_ptr_t *ptr, void *oldv, void *newv);
static _opa_inline int OPA_cas_int(OPA_int_t *ptr, int oldv, int newv);
static _opa_inline void *OPA_swap_ptr(OPA_ptr_t *ptr, void *val);
static _opa_inline int OPA_swap_int(OPA_int_t *ptr, int val);
// (the memory barriers may be macros instead of inline functions)
static _opa_inline void OPA_write_barrier();
static _opa_inline void OPA_read_barrier();
static _opa_inline void OPA_read_write_barrier();
// Loads and stores with memory ordering guarantees (also may be macros):
static _opa_inline int OPA_load_acquire_int(_opa_const OPA_int_t *ptr);
static _opa_inline void OPA_store_release_int(OPA_int_t *ptr, int val);
static _opa_inline void *OPA_load_acquire_ptr(_opa_const OPA_ptr_t *ptr);
static _opa_inline void OPA_store_release_ptr(OPA_ptr_t *ptr, void *val);
// Compiler barrier, only preventing compiler reordering, *not* CPU
// reordering (may be a macro):
static _opa_inline void OPA_compiler_barrier();
// The following need to be ported only for architectures supporting LL/SC:
static _opa_inline int OPA_LL_int(OPA_int_t *ptr);
static _opa_inline int OPA_SC_int(OPA_int_t *ptr, int val);
static _opa_inline void *OPA_LL_ptr(OPA_ptr_t *ptr);
static _opa_inline int OPA_SC_ptr(OPA_ptr_t *ptr, void *val);
// Additionally, the following initializer macros must be defined:
#define OPA_INT_T_INITIALIZER(val_) ...
#define OPA_PTR_T_INITIALIZER(val_) ...
// They should be useable as C89 static initializers like so:
struct { int x; OPA_int_t y; OPA_ptr_t z; } foo = { 35, OPA_INT_T_INITIALIZER(1), OPA_PTR_T_INITIALIZER(NULL) };
*/
/* Include the appropriate header for the architecture */
#if defined(OPA_USE_UNSAFE_PRIMITIVES)
/* comes first to permit user overrides in the style of NDEBUG */
#include "primitives/opa_unsafe.h"
#elif defined(OPA_HAVE_GCC_AND_POWERPC_ASM)
#include "primitives/opa_gcc_ppc.h"
#elif defined(OPA_HAVE_GCC_AND_ARM_ASM)
#include "primitives/opa_gcc_arm.h"
#elif defined(OPA_HAVE_GCC_X86_32_64)
#include "primitives/opa_gcc_intel_32_64.h"
#elif defined(OPA_HAVE_GCC_X86_32_64_P3)
#include "primitives/opa_gcc_intel_32_64_p3.h"
#elif defined(OPA_HAVE_GCC_AND_IA64_ASM)
#include "primitives/opa_gcc_ia64.h"
#elif defined(OPA_HAVE_GCC_AND_SICORTEX_ASM)
#include "primitives/opa_gcc_sicortex.h"
#elif defined(OPA_HAVE_GCC_INTRINSIC_ATOMICS)
#include "primitives/opa_gcc_intrinsics.h"
#elif defined(OPA_HAVE_SUN_ATOMIC_OPS)
#include "primitives/opa_sun_atomic_ops.h"
#elif defined(OPA_HAVE_NT_INTRINSICS)
#include "primitives/opa_nt_intrinsics.h"
#elif defined(OPA_USE_LOCK_BASED_PRIMITIVES)
#include "primitives/opa_by_lock.h"
#else
#error no primitives implementation specified
#endif
/*
This routine is needed because the MPID_THREAD_XXX_CS_{ENTER,EXIT} macros do
not provide synchronization across multiple processes, only across multiple
threads within a process. In order to safely emulate atomic operations on a
shared memory region, we need a shared memory backed lock mechanism.
This routine must be called by any subsystem that intends to use the atomic
abstractions if the cpp directive OPA_USE_LOCK_BASED_PRIMITIVES is defined. It must
be called exactly once by _all_ processes, not just a single leader. This
function will initialize the contents of the lock variable if the caller
specifies (isLeader==true). Note that multiple initialization is forbidden
by several lock implementations, especially pthreads.
Inputs:
shm_lock - A pointer to an allocated piece of shared memory that can hold
a mutex (e.g., pthread_mutex_t). This is not portable to
non-pthreads systems at this time.
isLeader - This boolean value should be set to true for exactly one
thread/process of the group that calls this function.
*/
#if defined(OPA_HAVE_PTHREAD_H)
#include <pthread.h>
typedef pthread_mutex_t OPA_emulation_ipl_t;
int OPA_Interprocess_lock_init(OPA_emulation_ipl_t * shm_lock, int isLeader);
#endif
/* FIXME This should probably be pushed down into the platform-specific headers. */
#if defined(OPA_HAVE_SCHED_YIELD)
#include <sched.h>
#define OPA_busy_wait() sched_yield()
#else
#define OPA_busy_wait() do { } while (0)
#endif
#endif /* OPA_PRIMITIVES_H_INCLUDED */