#if defined(HAVE_CONFIG_H)
# include "config.h"
#endif
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#ifndef AO_BUILD
# define AO_BUILD
#endif
#ifndef AO_REAL_PTR_AS_MACRO
# define AO_REAL_PTR_AS_MACRO
#endif
#define AO_REQUIRE_CAS
#include "atomic_ops_stack.h"
AO_API void AO_stack_init(AO_stack_t *list)
{
memset(list, 0, sizeof(AO_stack_t));
}
AO_API int AO_stack_is_lock_free(void)
{
# ifdef AO_USE_ALMOST_LOCK_FREE
return 0;
# else
return 1;
# endif
}
AO_API AO_uintptr_t *AO_stack_head_ptr(const AO_stack_t *list)
{
return AO_REAL_HEAD_PTR(*list);
}
AO_API AO_uintptr_t *AO_stack_next_ptr_d(const AO_uintptr_t *pnext)
{
return AO_REAL_NEXT_PTR(*pnext);
}
AO_API AO_uintptr_t *AO_stack_next_ptr(AO_uintptr_t next)
{
return AO_REAL_NEXT_PTR(next);
}
#ifdef AO_THREAD_SANITIZER
AO_ATTR_NO_SANITIZE_THREAD
static void store_before_cas(AO_internal_ptr_t *addr,
AO_internal_ptr_t value)
{
*addr = value;
}
#else
# define store_before_cas(addr, value) (void)(*(addr) = (value))
#endif
#ifdef AO_USE_ALMOST_LOCK_FREE
# ifdef __cplusplus
extern "C" {
# endif
AO_API void AO_pause(int);
# ifdef __cplusplus
}
# endif
# if defined(__alpha__) && (__GNUC__ == 4)
# undef AO_EXPECT_FALSE
# define AO_EXPECT_FALSE(expr) (expr)
# endif
# if defined(AO_FAT_POINTER) || defined(AO_STACK_USE_CPTR)
AO_INLINE int
AO_cptr_compare_and_swap_acquire(AO_internal_ptr_t volatile *addr,
AO_internal_ptr_t old_val, AO_internal_ptr_t new_val)
{
return (int)__atomic_compare_exchange_n(addr, &old_val, new_val, 0,
__ATOMIC_ACQUIRE, __ATOMIC_ACQUIRE);
}
AO_INLINE int
AO_cptr_compare_and_swap_release(AO_internal_ptr_t volatile *addr,
AO_internal_ptr_t old_val, AO_internal_ptr_t new_val)
{
return (int)__atomic_compare_exchange_n(addr, &old_val, new_val, 0,
__ATOMIC_RELEASE, __ATOMIC_RELAXED );
}
# define AO_cptr_load(p) __atomic_load_n(p, __ATOMIC_RELAXED)
# define AO_cptr_load_acquire(p) __atomic_load_n(p, __ATOMIC_ACQUIRE)
# define AO_cptr_store_release(p, v) \
__atomic_store_n(p, v, __ATOMIC_RELEASE)
# else
# define AO_cptr_compare_and_swap_acquire AO_compare_and_swap_acquire
# define AO_cptr_compare_and_swap_release AO_compare_and_swap_release
# define AO_cptr_load AO_load
# define AO_cptr_load_acquire AO_load_acquire
# define AO_cptr_store_release AO_store_release
# endif
AO_API void AO_stack_push_explicit_aux_release(volatile AO_uintptr_t *list,
AO_uintptr_t *x,
AO_stack_aux *a)
{
AO_internal_ptr_t x_bits = (AO_internal_ptr_t)x;
AO_internal_ptr_t next;
retry:
do {
next = AO_cptr_load_acquire((AO_internal_ptr_t volatile *)list);
store_before_cas((AO_internal_ptr_t *)x, next);
{
# if AO_BL_SIZE == 2
AO_internal_ptr_t entry1 = AO_cptr_load(&a->AO_stack_bl[0]);
AO_internal_ptr_t entry2 = AO_cptr_load(&a->AO_stack_bl[1]);
if (AO_EXPECT_FALSE(entry1 == x_bits || entry2 == x_bits))
# else
int i;
for (i = 0; i < AO_BL_SIZE; ++i)
if (AO_EXPECT_FALSE(AO_cptr_load(&a->AO_stack_bl[i]) == x_bits))
# endif
{
++x_bits;
if (((AO_uintptr_t)x_bits & AO_BIT_MASK) == 0)
x_bits = (AO_internal_ptr_t)x;
goto retry;
}
}
} while (AO_EXPECT_FALSE(!AO_cptr_compare_and_swap_release(
(AO_internal_ptr_t volatile *)list, next, x_bits)));
}
# ifdef __i386__
# define PRECHECK(a) (a) == 0 &&
# else
# define PRECHECK(a)
# endif
# ifdef AO_THREAD_SANITIZER
AO_ATTR_NO_SANITIZE_THREAD
static AO_internal_ptr_t load_next(
AO_internal_ptr_t const volatile *first_ptr)
{
return *first_ptr;
}
# else
# define load_next AO_cptr_load
# endif
AO_API AO_uintptr_t *AO_stack_pop_explicit_aux_acquire(
volatile AO_uintptr_t *list,
AO_stack_aux *a)
{
unsigned i;
int j = 0;
AO_internal_ptr_t first, next;
AO_internal_ptr_t *first_ptr;
retry:
first = AO_cptr_load((AO_internal_ptr_t volatile *)list);
if (0 == first) return NULL;
for (i = 0; ; )
{
if (PRECHECK(a->AO_stack_bl[i])
AO_cptr_compare_and_swap_acquire(a->AO_stack_bl+i, 0, first))
break;
if (++i >= AO_BL_SIZE)
{
i = 0;
AO_pause(++j);
}
}
# ifndef AO_THREAD_SANITIZER
assert(a->AO_stack_bl[i] == first);
# endif
if (AO_EXPECT_FALSE(first != AO_cptr_load_acquire(
(AO_internal_ptr_t volatile *)list)))
{
AO_cptr_store_release(a->AO_stack_bl+i, 0);
goto retry;
}
first_ptr = (AO_internal_ptr_t *)AO_REAL_NEXT_PTR(*(AO_uintptr_t *)&first);
next = load_next(first_ptr);
if (AO_EXPECT_FALSE(!AO_cptr_compare_and_swap_release(
(AO_internal_ptr_t volatile *)list,
first, next)))
{
AO_cptr_store_release(a->AO_stack_bl+i, 0);
goto retry;
}
# ifndef AO_THREAD_SANITIZER
assert(*(AO_internal_ptr_t *)list != first);
# endif
AO_cptr_store_release(a->AO_stack_bl+i, 0);
return (AO_uintptr_t *)first_ptr;
}
AO_API void AO_stack_push_release(AO_stack_t *list, AO_uintptr_t *x)
{
AO_stack_push_explicit_aux_release(
(volatile AO_uintptr_t *)&list->AO_pa.AO_ptr,
x, &list->AO_pa.AO_aux);
}
AO_API AO_uintptr_t *AO_stack_pop_acquire(AO_stack_t *list)
{
return AO_stack_pop_explicit_aux_acquire(
(volatile AO_uintptr_t *)&list->AO_pa.AO_ptr,
&list->AO_pa.AO_aux);
}
#else
# if defined(AO_THREAD_SANITIZER) \
&& defined(AO_HAVE_compare_double_and_swap_double)
# if defined(__clang__)
# define LOAD_BEFORE_CAS_VOLATILE volatile
# else
# define LOAD_BEFORE_CAS_VOLATILE
# endif
AO_ATTR_NO_SANITIZE_THREAD
static AO_t load_before_cas(const LOAD_BEFORE_CAS_VOLATILE AO_t *addr)
{
return *addr;
}
# else
# define load_before_cas(addr) (*(addr))
# endif
# define version AO_vp.AO_val1
# define ptr AO_vp.AO_val2
# ifdef LINT2
volatile AO_t AO_noop_sink;
# endif
AO_API void AO_stack_push_release(AO_stack_t *list, AO_uintptr_t *element)
{
AO_t next;
do {
next = AO_load(&list->ptr);
store_before_cas(element, next);
} while (AO_EXPECT_FALSE(!AO_compare_and_swap_release(&list->ptr, next,
(AO_t)element)));
# ifdef LINT2
AO_noop_sink = (AO_t)element;
# endif
}
AO_API AO_uintptr_t *AO_stack_pop_acquire(AO_stack_t *list)
{
# if defined(__clang__) && !AO_CLANG_PREREQ(3, 5)
AO_t *volatile cptr;
# else
AO_t *cptr;
# endif
AO_t next;
AO_t cversion;
do {
cversion = AO_load_acquire(&list->version);
cptr = (AO_t *)AO_load(&list->ptr);
if (NULL == cptr)
break;
next = load_before_cas(( AO_t *)cptr);
} while (AO_EXPECT_FALSE(!AO_compare_double_and_swap_double_release(
&list->AO_vp, cversion, (AO_t)cptr,
cversion+1, next)));
return (AO_uintptr_t *)cptr;
}
# undef ptr
# undef version
#endif