/*
 * Copyright (c) 2005 Hewlett-Packard Development Company, L.P.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/*
 * The implementation of the routines described here is covered by the GPL.
 * This header file is covered by the above license.
 */

/* Almost lock-free LIFO linked lists (linked stacks).  */
#ifndef AO_STACK_H
#define AO_STACK_H

#include "atomic_ops.h"

#ifndef AO_HAVE_double_t
  /* Can happen if we are using CAS emulation, since we do not want to  */
  /* force that here, in case other atomic_ops clients do not want it.  */
# include "atomic_ops/sysdeps/standard_ao_double_t.h"
#endif

#ifdef __cplusplus
  extern "C" {
#endif

#ifdef AO_USE_ALMOST_LOCK_FREE
  /* Use the almost-non-blocking implementation regardless of the       */
  /* compare-and-swap availability.                                     */
#elif defined(AO_HAVE_compare_double_and_swap_double) \
      && !defined(AO_FAT_POINTER) && !defined(AO_STACK_USE_CPTR)
# define AO_STACK_IS_LOCK_FREE
#elif !defined(AO_HAVE_compare_and_swap)
  /* If we have no compare-and-swap operation at all, we assume that we */
  /* will actually be emulating it.                                     */
# define AO_STACK_IS_LOCK_FREE
#else
# define AO_USE_ALMOST_LOCK_FREE
  /* If we do have a real compare-and-swap operation, it is cheaper to  */
  /* use the version-based implementation.                              */
#endif

/*
 * These are not guaranteed to be completely lock-free.
 * List insertion may spin under extremely unlikely conditions.
 * It cannot deadlock due to recursive reentry unless AO_list_remove
 * is called while at least AO_BL_SIZE activations of
 * AO_list_remove are currently active in the same thread, i.e.
 * we must have at least AO_BL_SIZE recursive signal handler
 * invocations.
 *
 * All operations take an AO_list_aux argument.  It is safe to
 * share a single AO_list_aux structure among all lists, but that
 * may increase contention.  Any given list must always be accessed
 * with the same AO_list_aux structure.
 *
 * We make some machine-dependent assumptions:
 *   - we have a compare-and-swap operation;
 *   - at least AO_N_BITS low order bits in pointers are
 *     zero and normally unused.
 *
 * We do use a fully lock-free implementation if double-width
 * compare-and-swap operations are available.
 */

/* AO_stack_aux should be treated as opaque.  It is fully defined       */
/* here, so it can be allocated, and also to facilitate debugging.      */
/* Note: changing the value of AO_BL_SIZE leads to the ABI change.      */
#ifndef AO_BL_SIZE
# define AO_BL_SIZE 2
#endif

/* The number of low order pointer bits we can use for a small          */
/* version number.                                                      */
#if defined(AO_FAT_POINTER) && defined(__LP64__)
# define AO_N_BITS 4
#elif defined(__LP64__) || defined(_LP64) || defined(_WIN64) \
      || defined(AO_FAT_POINTER)
# define AO_N_BITS 3
#else
# define AO_N_BITS 2
#endif

#define AO_BIT_MASK ((1 << AO_N_BITS) - 1)

#if AO_BL_SIZE > (1 << AO_N_BITS)
# error AO_BL_SIZE too big
#endif

#ifndef AO_STACK_ATTR_ALLIGNED
  /* Enforce proper alignment of AO_stack_t.AO_pa to avoid the          */
  /* structure value to cross the CPU cache line boundary.              */
  /* A workaround for almost-lock-free push/pop test failures           */
  /* on aarch64, at least.                                              */
# if AO_BL_SIZE == 1
    /* AO_vp is double-pointer aligned, no extra align of AO_pa is needed.  */
#   define AO_STACK_ATTR_ALLIGNED /* empty */
# elif AO_GNUC_PREREQ(3, 1)
#   define AO_STACK_LOG_BL_SZP1 (AO_BL_SIZE > 7 ? 4 : AO_BL_SIZE > 3 ? 3 : 2)
#   define AO_STACK_ATTR_ALLIGNED \
        __attribute__((__aligned__(sizeof(void*) << AO_STACK_LOG_BL_SZP1)))
# elif defined(_MSC_VER) && _MSC_VER >= 1400 /* Visual Studio 2005+ */
    /* MS compiler accepts only a literal number in align, not expression.  */
    /* AO_STACK_ALLIGN_N is 1 << (AO_N_BITS + AO_STACK_LOG_BL_SZP1).        */
#   if AO_N_BITS > 2 && AO_BL_SIZE > 7
#     define AO_STACK_ALLIGN_N 128
#   elif (AO_N_BITS > 2 && AO_BL_SIZE > 3) || AO_BL_SIZE > 7
#     define AO_STACK_ALLIGN_N 64
#   elif AO_N_BITS > 2 || AO_BL_SIZE > 3
#     define AO_STACK_ALLIGN_N 32
#   else
#     define AO_STACK_ALLIGN_N 16
#   endif
#   define AO_STACK_ATTR_ALLIGNED __declspec(align(AO_STACK_ALLIGN_N))
# else
#   define AO_STACK_ATTR_ALLIGNED /* TODO: alignment is not enforced */
# endif
#endif /* !AO_STACK_ATTR_ALLIGNED */

#if defined(__e2k__) && defined(AO_FAT_POINTER) || defined(AO_STACK_USE_CPTR)
  /* A workaround because the platform does not allow conversion from   */
  /* a numeric type (128-bit one) to a pointer.                         */
  typedef char *AO_internal_ptr_t;
# ifndef AO_STACK_USE_CPTR
#   define AO_STACK_USE_CPTR
# endif
#else
  typedef AO_uintptr_t AO_internal_ptr_t;
#endif

typedef struct AO__stack_aux {
  AO_internal_ptr_t volatile AO_stack_bl[AO_BL_SIZE];
} AO_stack_aux;

struct AO__stack_ptr_aux {
  AO_internal_ptr_t volatile AO_ptr;
  AO_stack_aux AO_aux;
};

/* The AO stack type.  Should be treated as opaque.                     */
/* Note: AO_stack_t variables are not intended to be local ones,        */
/* otherwise it is the client responsibility to ensure they have        */
/* double-word alignment.                                               */
typedef union AO__stack {
  AO_STACK_ATTR_ALLIGNED struct AO__stack_ptr_aux AO_pa;
  volatile AO_double_t AO_vp;
} AO_stack_t;

/* The static initializer of the AO stack type. */
#define AO_STACK_INITIALIZER { /* .AO_pa= */ { 0, { {0} } } }

#ifdef AO_USE_ALMOST_LOCK_FREE
  /* The following two routines should not normally be used directly.   */
  /* We make them visible here for the rare cases in which it makes     */
  /* sense to share the AO_stack_aux between stacks.                    */

  AO_API void AO_stack_push_explicit_aux_release(
                                        volatile AO_uintptr_t * /* list */,
                                        AO_uintptr_t * /* new_element */,
                                        AO_stack_aux *);

  AO_API AO_uintptr_t *AO_stack_pop_explicit_aux_acquire(
                                        volatile AO_uintptr_t * /* list */,
                                        AO_stack_aux *);
#endif /* AO_USE_ALMOST_LOCK_FREE */

#ifndef AO_REAL_PTR_AS_MACRO
  /* The stack implementation knows only about the location of  */
  /* link fields in nodes, and nothing about the rest of the    */
  /* stack elements.  Link fields hold an AO_uintptr_t, which   */
  /* is not necessarily a real pointer.  This converts the      */
  /* AO_uintptr_t value to a real AO_uintptr_t* which is either */
  /* NULL, or points at the link field in the next node.        */
# define AO_REAL_NEXT_PTR(x) AO_stack_next_ptr_d(&(x))

  /* Convert an AO_stack_t to a pointer to the link field in    */
  /* the first element.                                         */
# define AO_REAL_HEAD_PTR(x) AO_stack_head_ptr(&(x))

#elif defined(AO_USE_ALMOST_LOCK_FREE)
# ifdef AO_STACK_USE_CPTR
#   define AO_REAL_NEXT_PTR(x) \
        ((AO_uintptr_t *)AO_real_next_ptr_i(*(const AO_internal_ptr_t *)&(x)))
    /* Implemented as an inline function because the argument is used twice. */
    AO_INLINE AO_internal_ptr_t
    AO_real_next_ptr_i(AO_internal_ptr_t next)
    {
      return next - ((unsigned)(AO_uintptr_t)next & AO_BIT_MASK);
    }
# else
#   define AO_REAL_NEXT_PTR(x) \
            (AO_uintptr_t *)((x) & ~(AO_uintptr_t)AO_BIT_MASK)
# endif
# define AO_REAL_HEAD_PTR(x) \
            AO_REAL_NEXT_PTR(*(volatile AO_uintptr_t *)&(&(x))->AO_pa.AO_ptr)
#else
# define AO_REAL_NEXT_PTR(x) ((AO_t *)*(&(x)))
# define AO_REAL_HEAD_PTR(x) (AO_t *)((&(x))->AO_vp.AO_val2 /* ptr */)
#endif /* AO_REAL_PTR_AS_MACRO && !AO_USE_ALMOST_LOCK_FREE */

AO_API void AO_stack_push_release(AO_stack_t *,
                                  AO_uintptr_t * /* new_element */);
#define AO_HAVE_stack_push_release

#define AO_stack_push(l, e) AO_stack_push_release(l, e)
#define AO_HAVE_stack_push

AO_API AO_uintptr_t *AO_stack_pop_acquire(AO_stack_t *);
#define AO_HAVE_stack_pop_acquire

#define AO_stack_pop(l) AO_stack_pop_acquire(l)
#define AO_HAVE_stack_pop

AO_API void AO_stack_init(AO_stack_t *);
AO_API int AO_stack_is_lock_free(void);

/* These primitives should not be used directly.        */
AO_API AO_uintptr_t *AO_stack_head_ptr(const AO_stack_t *);
AO_API AO_uintptr_t *AO_stack_next_ptr(AO_uintptr_t); /* deprecated */
AO_API AO_uintptr_t *AO_stack_next_ptr_d(const AO_uintptr_t * /* pnext */);

#ifdef __cplusplus
  } /* extern "C" */
#endif

#endif /* !AO_STACK_H */
