/*
 * Copyright 2009-present MongoDB, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <common-prelude.h>


#ifndef MONGO_C_DRIVER_COMMON_ATOMIC_PRIVATE_H
#define MONGO_C_DRIVER_COMMON_ATOMIC_PRIVATE_H


#include <bson/bson.h> // BSON_INLINE

#ifdef _MSC_VER
#include <intrin.h>
#endif


enum mcommon_memory_order {
   mcommon_memory_order_seq_cst,
   mcommon_memory_order_acquire,
   mcommon_memory_order_release,
   mcommon_memory_order_relaxed,
   mcommon_memory_order_acq_rel,
   mcommon_memory_order_consume,
};

#if defined(_M_ARM) /* MSVC memorder atomics are only avail on ARM */
#define MSVC_MEMORDER_SUFFIX(X) X
#else
#define MSVC_MEMORDER_SUFFIX(X)
#endif

#if defined(USE_LEGACY_GCC_ATOMICS) || (!defined(__clang__) && __GNUC__ == 4) || defined(__xlC__)
#define MCOMMON_USE_LEGACY_GCC_ATOMICS
#else
#undef MCOMMON_USE_LEGACY_GCC_ATOMICS
#endif

/* Not all GCC-like compilers support the current __atomic built-ins.  Older
 * GCC (pre-5) used different built-ins named with the __sync prefix.  When
 * compiling with such older GCC versions, it is necessary to use the applicable
 * functions, which requires redefining BSON_IF_GNU_LIKE and defining the
 * additional MCOMMON_IF_GNU_LEGACY_ATOMICS macro here. */
#ifdef MCOMMON_USE_LEGACY_GCC_ATOMICS
#undef BSON_IF_GNU_LIKE
#define BSON_IF_GNU_LIKE(...)
#define MCOMMON_IF_GNU_LEGACY_ATOMICS(...) __VA_ARGS__
#else
#define MCOMMON_IF_GNU_LEGACY_ATOMICS(...)
#endif

/* CDRIVER-4229 zSeries with gcc 4.8.4 produces illegal instructions for int and
 * int32 atomic intrinsics. */
#if defined(__s390__) || defined(__s390x__) || defined(__zarch__)
#define MCOMMON_EMULATE_INT32
#define MCOMMON_EMULATE_INT
#endif

/* CDRIVER-4264 Contrary to documentation, VS 2013 targeting x86 does not
 * correctly/consistently provide _InterlockedPointerExchange. */
#if defined(_MSC_VER) && _MSC_VER < 1900 && defined(_M_IX86)
#define MCOMMON_EMULATE_PTR
#endif

#define DEF_ATOMIC_OP(MSVC_Intrinsic, GNU_Intrinsic, GNU_Legacy_Intrinsic, Order, ...)              \
   do {                                                                                             \
      switch (Order) {                                                                              \
      case mcommon_memory_order_acq_rel:                                                            \
         BSON_IF_MSVC(return MSVC_Intrinsic(__VA_ARGS__);)                                          \
         BSON_IF_GNU_LIKE(return GNU_Intrinsic(__VA_ARGS__, __ATOMIC_ACQ_REL);)                     \
         MCOMMON_IF_GNU_LEGACY_ATOMICS(return GNU_Legacy_Intrinsic(__VA_ARGS__);)                   \
      case mcommon_memory_order_seq_cst:                                                            \
         BSON_IF_MSVC(return MSVC_Intrinsic(__VA_ARGS__);)                                          \
         BSON_IF_GNU_LIKE(return GNU_Intrinsic(__VA_ARGS__, __ATOMIC_SEQ_CST);)                     \
         MCOMMON_IF_GNU_LEGACY_ATOMICS(return GNU_Legacy_Intrinsic(__VA_ARGS__);)                   \
      case mcommon_memory_order_acquire:                                                            \
         BSON_IF_MSVC(return BSON_CONCAT(MSVC_Intrinsic, MSVC_MEMORDER_SUFFIX(_acq))(__VA_ARGS__);) \
         BSON_IF_GNU_LIKE(return GNU_Intrinsic(__VA_ARGS__, __ATOMIC_ACQUIRE);)                     \
         MCOMMON_IF_GNU_LEGACY_ATOMICS(return GNU_Legacy_Intrinsic(__VA_ARGS__);)                   \
      case mcommon_memory_order_consume:                                                            \
         BSON_IF_MSVC(return BSON_CONCAT(MSVC_Intrinsic, MSVC_MEMORDER_SUFFIX(_acq))(__VA_ARGS__);) \
         BSON_IF_GNU_LIKE(return GNU_Intrinsic(__VA_ARGS__, __ATOMIC_CONSUME);)                     \
         MCOMMON_IF_GNU_LEGACY_ATOMICS(return GNU_Legacy_Intrinsic(__VA_ARGS__);)                   \
      case mcommon_memory_order_release:                                                            \
         BSON_IF_MSVC(return BSON_CONCAT(MSVC_Intrinsic, MSVC_MEMORDER_SUFFIX(_rel))(__VA_ARGS__);) \
         BSON_IF_GNU_LIKE(return GNU_Intrinsic(__VA_ARGS__, __ATOMIC_RELEASE);)                     \
         MCOMMON_IF_GNU_LEGACY_ATOMICS(return GNU_Legacy_Intrinsic(__VA_ARGS__);)                   \
      case mcommon_memory_order_relaxed:                                                            \
         BSON_IF_MSVC(return BSON_CONCAT(MSVC_Intrinsic, MSVC_MEMORDER_SUFFIX(_nf))(__VA_ARGS__);)  \
         BSON_IF_GNU_LIKE(return GNU_Intrinsic(__VA_ARGS__, __ATOMIC_RELAXED);)                     \
         MCOMMON_IF_GNU_LEGACY_ATOMICS(return GNU_Legacy_Intrinsic(__VA_ARGS__);)                   \
      default:                                                                                      \
         BSON_UNREACHABLE("Invalid mcommon_memory_order value");                                    \
      }                                                                                             \
   } while (0)


#define DEF_ATOMIC_CMPEXCH_STRONG(VCSuffix1, VCSuffix2, GNU_MemOrder, Ptr, ExpectActualVar, NewValue)   \
   do {                                                                                                 \
      BSON_IF_MSVC(ExpectActualVar = BSON_CONCAT3(_InterlockedCompareExchange, VCSuffix1, VCSuffix2)(   \
                      Ptr, NewValue, ExpectActualVar);)                                                 \
      BSON_IF_GNU_LIKE((void)__atomic_compare_exchange_n(Ptr,                                           \
                                                         &ExpectActualVar,                              \
                                                         NewValue,                                      \
                                                         false, /* Not weak */                          \
                                                         GNU_MemOrder,                                  \
                                                         GNU_MemOrder);)                                \
      MCOMMON_IF_GNU_LEGACY_ATOMICS(__typeof__(ExpectActualVar) _val;                                   \
                                    _val = __sync_val_compare_and_swap(Ptr, ExpectActualVar, NewValue); \
                                    ExpectActualVar = _val;)                                            \
   } while (0)


#define DEF_ATOMIC_CMPEXCH_WEAK(VCSuffix1, VCSuffix2, GNU_MemOrder, Ptr, ExpectActualVar, NewValue)     \
   do {                                                                                                 \
      BSON_IF_MSVC(ExpectActualVar = BSON_CONCAT3(_InterlockedCompareExchange, VCSuffix1, VCSuffix2)(   \
                      Ptr, NewValue, ExpectActualVar);)                                                 \
      BSON_IF_GNU_LIKE((void)__atomic_compare_exchange_n(Ptr,                                           \
                                                         &ExpectActualVar,                              \
                                                         NewValue,                                      \
                                                         true, /* Yes weak */                           \
                                                         GNU_MemOrder,                                  \
                                                         GNU_MemOrder);)                                \
      MCOMMON_IF_GNU_LEGACY_ATOMICS(__typeof__(ExpectActualVar) _val;                                   \
                                    _val = __sync_val_compare_and_swap(Ptr, ExpectActualVar, NewValue); \
                                    ExpectActualVar = _val;)                                            \
   } while (0)


// on Windows, when invoking the appropriate intrinsic, cast to the type the intrinsic is declared with to avoid
// warnings irrespective of the signedness of Type; all these operations are bit-level and don't care about the sign
#define DECL_ATOMIC_INTEGRAL(NamePart, Type, VCIntrinType, VCIntrinSuffix)                                            \
   static BSON_INLINE Type mcommon_atomic_##NamePart##_fetch_add(                                                     \
      Type volatile *a, Type addend, enum mcommon_memory_order ord)                                                   \
   {                                                                                                                  \
      DEF_ATOMIC_OP(BSON_CONCAT(_InterlockedExchangeAdd, VCIntrinSuffix),                                             \
                    __atomic_fetch_add,                                                                               \
                    __sync_fetch_and_add,                                                                             \
                    ord,                                                                                              \
                    BSON_IF_MSVC((volatile VCIntrinType *)) a,                                                        \
                    addend);                                                                                          \
   }                                                                                                                  \
                                                                                                                      \
   static BSON_INLINE Type mcommon_atomic_##NamePart##_fetch_sub(                                                     \
      Type volatile *a, Type subtrahend, enum mcommon_memory_order ord)                                               \
   {                                                                                                                  \
      /* MSVC doesn't have a subtract intrinsic, so just reuse addition    */                                         \
      BSON_IF_MSVC(return mcommon_atomic_##NamePart##_fetch_add(a, -subtrahend, ord);)                                \
      BSON_IF_GNU_LIKE(DEF_ATOMIC_OP(~, __atomic_fetch_sub, ~, ord, a, subtrahend);)                                  \
      MCOMMON_IF_GNU_LEGACY_ATOMICS(DEF_ATOMIC_OP(~, ~, __sync_fetch_and_sub, ord, a, subtrahend);)                   \
   }                                                                                                                  \
                                                                                                                      \
   static BSON_INLINE Type mcommon_atomic_##NamePart##_fetch(Type volatile const *a, enum mcommon_memory_order order) \
   {                                                                                                                  \
      /* MSVC doesn't have a load intrinsic, so just add zero */                                                      \
      BSON_IF_MSVC(return mcommon_atomic_##NamePart##_fetch_add((Type volatile *)a, 0, order);)                       \
      /* GNU doesn't want RELEASE order for the fetch operation, so we can't                                          \
       * just use DEF_ATOMIC_OP. */                                                                                   \
      BSON_IF_GNU_LIKE(switch (order) {                                                                               \
         case mcommon_memory_order_release: /* Fall back to seqcst */                                                 \
         case mcommon_memory_order_acq_rel: /* Fall back to seqcst */                                                 \
         case mcommon_memory_order_seq_cst:                                                                           \
            return __atomic_load_n(a, __ATOMIC_SEQ_CST);                                                              \
         case mcommon_memory_order_acquire:                                                                           \
            return __atomic_load_n(a, __ATOMIC_ACQUIRE);                                                              \
         case mcommon_memory_order_consume:                                                                           \
            return __atomic_load_n(a, __ATOMIC_CONSUME);                                                              \
         case mcommon_memory_order_relaxed:                                                                           \
            return __atomic_load_n(a, __ATOMIC_RELAXED);                                                              \
         default:                                                                                                     \
            BSON_UNREACHABLE("Invalid mcommon_memory_order value");                                                   \
      })                                                                                                              \
      MCOMMON_IF_GNU_LEGACY_ATOMICS({                                                                                 \
         BSON_UNUSED(order);                                                                                          \
         __sync_synchronize();                                                                                        \
         return *a;                                                                                                   \
      })                                                                                                              \
   }                                                                                                                  \
                                                                                                                      \
   static BSON_INLINE Type mcommon_atomic_##NamePart##_exchange(                                                      \
      Type volatile *a, Type value, enum mcommon_memory_order ord)                                                    \
   {                                                                                                                  \
      BSON_IF_MSVC(DEF_ATOMIC_OP(BSON_CONCAT(_InterlockedExchange, VCIntrinSuffix),                                   \
                                 ~,                                                                                   \
                                 ~,                                                                                   \
                                 ord,                                                                                 \
                                 BSON_IF_MSVC((volatile VCIntrinType *)) a,                                           \
                                 value);)                                                                             \
      /* GNU doesn't want CONSUME order for the exchange operation, so we                                             \
       * cannot use DEF_ATOMIC_OP. */                                                                                 \
      BSON_IF_GNU_LIKE(switch (ord) {                                                                                 \
         case mcommon_memory_order_acq_rel:                                                                           \
            return __atomic_exchange_n(a, value, __ATOMIC_ACQ_REL);                                                   \
         case mcommon_memory_order_release:                                                                           \
            return __atomic_exchange_n(a, value, __ATOMIC_RELEASE);                                                   \
         case mcommon_memory_order_seq_cst:                                                                           \
            return __atomic_exchange_n(a, value, __ATOMIC_SEQ_CST);                                                   \
         case mcommon_memory_order_consume: /* Fall back to acquire */                                                \
         case mcommon_memory_order_acquire:                                                                           \
            return __atomic_exchange_n(a, value, __ATOMIC_ACQUIRE);                                                   \
         case mcommon_memory_order_relaxed:                                                                           \
            return __atomic_exchange_n(a, value, __ATOMIC_RELAXED);                                                   \
         default:                                                                                                     \
            BSON_UNREACHABLE("Invalid mcommon_memory_order value");                                                   \
      })                                                                                                              \
      MCOMMON_IF_GNU_LEGACY_ATOMICS(BSON_UNUSED(ord); return __sync_val_compare_and_swap(a, *a, value);)              \
   }                                                                                                                  \
                                                                                                                      \
   static BSON_INLINE Type mcommon_atomic_##NamePart##_compare_exchange_strong(                                       \
      Type volatile *a, Type expect, Type new_value, enum mcommon_memory_order ord)                                   \
   {                                                                                                                  \
      Type actual = expect;                                                                                           \
      switch (ord) {                                                                                                  \
      case mcommon_memory_order_release:                                                                              \
      case mcommon_memory_order_acq_rel:                                                                              \
      case mcommon_memory_order_seq_cst:                                                                              \
         DEF_ATOMIC_CMPEXCH_STRONG(                                                                                   \
            VCIntrinSuffix, , __ATOMIC_SEQ_CST, BSON_IF_MSVC((volatile VCIntrinType *)) a, actual, new_value);        \
         break;                                                                                                       \
      case mcommon_memory_order_acquire:                                                                              \
         DEF_ATOMIC_CMPEXCH_STRONG(VCIntrinSuffix,                                                                    \
                                   MSVC_MEMORDER_SUFFIX(_acq),                                                        \
                                   __ATOMIC_ACQUIRE,                                                                  \
                                   BSON_IF_MSVC((volatile VCIntrinType *)) a,                                         \
                                   actual,                                                                            \
                                   new_value);                                                                        \
         break;                                                                                                       \
      case mcommon_memory_order_consume:                                                                              \
         DEF_ATOMIC_CMPEXCH_STRONG(VCIntrinSuffix,                                                                    \
                                   MSVC_MEMORDER_SUFFIX(_acq),                                                        \
                                   __ATOMIC_CONSUME,                                                                  \
                                   BSON_IF_MSVC((volatile VCIntrinType *)) a,                                         \
                                   actual,                                                                            \
                                   new_value);                                                                        \
         break;                                                                                                       \
      case mcommon_memory_order_relaxed:                                                                              \
         DEF_ATOMIC_CMPEXCH_STRONG(VCIntrinSuffix,                                                                    \
                                   MSVC_MEMORDER_SUFFIX(_nf),                                                         \
                                   __ATOMIC_RELAXED,                                                                  \
                                   BSON_IF_MSVC((volatile VCIntrinType *)) a,                                         \
                                   actual,                                                                            \
                                   new_value);                                                                        \
         break;                                                                                                       \
      default:                                                                                                        \
         BSON_UNREACHABLE("Invalid mcommon_memory_order value");                                                      \
      }                                                                                                               \
      return actual;                                                                                                  \
   }                                                                                                                  \
                                                                                                                      \
   static BSON_INLINE Type mcommon_atomic_##NamePart##_compare_exchange_weak(                                         \
      Type volatile *a, Type expect, Type new_value, enum mcommon_memory_order ord)                                   \
   {                                                                                                                  \
      Type actual = expect;                                                                                           \
      switch (ord) {                                                                                                  \
      case mcommon_memory_order_release:                                                                              \
      case mcommon_memory_order_acq_rel:                                                                              \
      case mcommon_memory_order_seq_cst:                                                                              \
         DEF_ATOMIC_CMPEXCH_WEAK(                                                                                     \
            VCIntrinSuffix, , __ATOMIC_SEQ_CST, BSON_IF_MSVC((volatile VCIntrinType *)) a, actual, new_value);        \
         break;                                                                                                       \
      case mcommon_memory_order_acquire:                                                                              \
         DEF_ATOMIC_CMPEXCH_WEAK(VCIntrinSuffix,                                                                      \
                                 MSVC_MEMORDER_SUFFIX(_acq),                                                          \
                                 __ATOMIC_ACQUIRE,                                                                    \
                                 BSON_IF_MSVC((volatile VCIntrinType *)) a,                                           \
                                 actual,                                                                              \
                                 new_value);                                                                          \
         break;                                                                                                       \
      case mcommon_memory_order_consume:                                                                              \
         DEF_ATOMIC_CMPEXCH_WEAK(VCIntrinSuffix,                                                                      \
                                 MSVC_MEMORDER_SUFFIX(_acq),                                                          \
                                 __ATOMIC_CONSUME,                                                                    \
                                 BSON_IF_MSVC((volatile VCIntrinType *)) a,                                           \
                                 actual,                                                                              \
                                 new_value);                                                                          \
         break;                                                                                                       \
      case mcommon_memory_order_relaxed:                                                                              \
         DEF_ATOMIC_CMPEXCH_WEAK(VCIntrinSuffix,                                                                      \
                                 MSVC_MEMORDER_SUFFIX(_nf),                                                           \
                                 __ATOMIC_RELAXED,                                                                    \
                                 BSON_IF_MSVC((volatile VCIntrinType *)) a,                                           \
                                 actual,                                                                              \
                                 new_value);                                                                          \
         break;                                                                                                       \
      default:                                                                                                        \
         BSON_UNREACHABLE("Invalid mcommon_memory_order value");                                                      \
      }                                                                                                               \
      return actual;                                                                                                  \
   }

#define DECL_ATOMIC_STDINT(Name, VCIntrinType, VCSuffix) DECL_ATOMIC_INTEGRAL(Name, Name##_t, VCIntrinType, VCSuffix)

#if defined(_MSC_VER) || defined(MCOMMON_USE_LEGACY_GCC_ATOMICS)
/* MSVC and GCC require built-in types (not typedefs) for their atomic
 * intrinsics.
 * When using clang-cl it pretends to be MSVC (by defining _MSC_VER)
 * but then complains about int8_t actual arguments (signed char on Windows) vs char formal arguments mismatch
 */
#if defined(_MSC_VER) && !defined(__clang__)
#define DECL_ATOMIC_INTEGRAL_INT8 char
#define DECL_ATOMIC_INTEGRAL_INT32 long
#define DECL_ATOMIC_INTEGRAL_INT long
#else
#define DECL_ATOMIC_INTEGRAL_INT8 signed char
#define DECL_ATOMIC_INTEGRAL_INT32 int
#define DECL_ATOMIC_INTEGRAL_INT int
#endif
DECL_ATOMIC_INTEGRAL(int8, DECL_ATOMIC_INTEGRAL_INT8, char, 8)
DECL_ATOMIC_INTEGRAL(int16, short, short, 16)
#if !defined(MCOMMON_EMULATE_INT32)
DECL_ATOMIC_INTEGRAL(int32, DECL_ATOMIC_INTEGRAL_INT32, long, )
#endif
#if !defined(MCOMMON_EMULATE_INT)
DECL_ATOMIC_INTEGRAL(int, DECL_ATOMIC_INTEGRAL_INT, long, )
#endif
#else
/* Other compilers that we support provide generic intrinsics */
DECL_ATOMIC_STDINT(int8, char, 8)
DECL_ATOMIC_STDINT(int16, short, 16)
#if !defined(MCOMMON_EMULATE_INT32)
DECL_ATOMIC_STDINT(int32, long, )
#endif
#if !defined(MCOMMON_EMULATE_INT)
DECL_ATOMIC_INTEGRAL(int, int, long, )
#endif
#endif

#ifndef DECL_ATOMIC_INTEGRAL_INT32
#define DECL_ATOMIC_INTEGRAL_INT32 int32_t
#endif

#define _mcommon_emul_atomic_int64_fetch_add COMMON_NAME(emul_atomic_int64_fetch_add)
#define _mcommon_emul_atomic_int64_exchange COMMON_NAME(emul_atomic_int64_exchange)
#define _mcommon_emul_atomic_int64_compare_exchange_strong COMMON_NAME(emul_atomic_int64_compare_exchange_strong)
#define _mcommon_emul_atomic_int64_compare_exchange_weak COMMON_NAME(emul_atomic_int64_compare_exchange_weak)
#define _mcommon_emul_atomic_int32_fetch_add COMMON_NAME(emul_atomic_int32_fetch_add)
#define _mcommon_emul_atomic_int32_exchange COMMON_NAME(emul_atomic_int32_exchange)
#define _mcommon_emul_atomic_int32_compare_exchange_strong COMMON_NAME(emul_atomic_int32_compare_exchange_strong)
#define _mcommon_emul_atomic_int32_compare_exchange_weak COMMON_NAME(emul_atomic_int32_compare_exchange_weak)
#define _mcommon_emul_atomic_int_fetch_add COMMON_NAME(emul_atomic_int_fetch_add)
#define _mcommon_emul_atomic_int_exchange COMMON_NAME(emul_atomic_int_exchange)
#define _mcommon_emul_atomic_int_compare_exchange_strong COMMON_NAME(emul_atomic_int_compare_exchange_strong)
#define _mcommon_emul_atomic_int_compare_exchange_weak COMMON_NAME(emul_atomic_int_compare_exchange_weak)
#define _mcommon_emul_atomic_ptr_exchange COMMON_NAME(emul_atomic_ptr_exchange)
#define mcommon_thrd_yield COMMON_NAME(thrd_yield)

int64_t
_mcommon_emul_atomic_int64_fetch_add(int64_t volatile *val, int64_t v, enum mcommon_memory_order);
int64_t
_mcommon_emul_atomic_int64_exchange(int64_t volatile *val, int64_t v, enum mcommon_memory_order);
int64_t
_mcommon_emul_atomic_int64_compare_exchange_strong(int64_t volatile *val,
                                                   int64_t expect_value,
                                                   int64_t new_value,
                                                   enum mcommon_memory_order);

int64_t
_mcommon_emul_atomic_int64_compare_exchange_weak(int64_t volatile *val,
                                                 int64_t expect_value,
                                                 int64_t new_value,
                                                 enum mcommon_memory_order);

int32_t
_mcommon_emul_atomic_int32_fetch_add(int32_t volatile *val, int32_t v, enum mcommon_memory_order);
int32_t
_mcommon_emul_atomic_int32_exchange(int32_t volatile *val, int32_t v, enum mcommon_memory_order);
int32_t
_mcommon_emul_atomic_int32_compare_exchange_strong(int32_t volatile *val,
                                                   int32_t expect_value,
                                                   int32_t new_value,
                                                   enum mcommon_memory_order);

int32_t
_mcommon_emul_atomic_int32_compare_exchange_weak(int32_t volatile *val,
                                                 int32_t expect_value,
                                                 int32_t new_value,
                                                 enum mcommon_memory_order);

int
_mcommon_emul_atomic_int_fetch_add(int volatile *val, int v, enum mcommon_memory_order);
int
_mcommon_emul_atomic_int_exchange(int volatile *val, int v, enum mcommon_memory_order);
int
_mcommon_emul_atomic_int_compare_exchange_strong(int volatile *val,
                                                 int expect_value,
                                                 int new_value,
                                                 enum mcommon_memory_order);

int
_mcommon_emul_atomic_int_compare_exchange_weak(int volatile *val,
                                               int expect_value,
                                               int new_value,
                                               enum mcommon_memory_order);

void *
_mcommon_emul_atomic_ptr_exchange(void *volatile *val, void *v, enum mcommon_memory_order);

void
mcommon_thrd_yield(void);

#if (defined(_MSC_VER) && !defined(_M_IX86)) || (defined(__LP64__) && __LP64__)
/* (64-bit intrinsics are only available in x64) */
#ifdef _MSC_VER
DECL_ATOMIC_INTEGRAL(int64, __int64, __int64, 64)
#else
DECL_ATOMIC_STDINT(int64, __int64, 64)
#endif
#else
static BSON_INLINE int64_t
mcommon_atomic_int64_fetch(const int64_t volatile *val, enum mcommon_memory_order order)
{
   return _mcommon_emul_atomic_int64_fetch_add((int64_t volatile *)val, 0, order);
}

static BSON_INLINE int64_t
mcommon_atomic_int64_fetch_add(int64_t volatile *val, int64_t v, enum mcommon_memory_order order)
{
   return _mcommon_emul_atomic_int64_fetch_add(val, v, order);
}

static BSON_INLINE int64_t
mcommon_atomic_int64_fetch_sub(int64_t volatile *val, int64_t v, enum mcommon_memory_order order)
{
   return _mcommon_emul_atomic_int64_fetch_add(val, -v, order);
}

static BSON_INLINE int64_t
mcommon_atomic_int64_exchange(int64_t volatile *val, int64_t v, enum mcommon_memory_order order)
{
   return _mcommon_emul_atomic_int64_exchange(val, v, order);
}

static BSON_INLINE int64_t
mcommon_atomic_int64_compare_exchange_strong(int64_t volatile *val,
                                             int64_t expect_value,
                                             int64_t new_value,
                                             enum mcommon_memory_order order)
{
   return _mcommon_emul_atomic_int64_compare_exchange_strong(val, expect_value, new_value, order);
}

static BSON_INLINE int64_t
mcommon_atomic_int64_compare_exchange_weak(int64_t volatile *val,
                                           int64_t expect_value,
                                           int64_t new_value,
                                           enum mcommon_memory_order order)
{
   return _mcommon_emul_atomic_int64_compare_exchange_weak(val, expect_value, new_value, order);
}
#endif

#if defined(MCOMMON_EMULATE_INT32)
static BSON_INLINE int32_t
mcommon_atomic_int32_fetch(const int32_t volatile *val, enum mcommon_memory_order order)
{
   return _mcommon_emul_atomic_int32_fetch_add((int32_t volatile *)val, 0, order);
}

static BSON_INLINE int32_t
mcommon_atomic_int32_fetch_add(int32_t volatile *val, int32_t v, enum mcommon_memory_order order)
{
   return _mcommon_emul_atomic_int32_fetch_add(val, v, order);
}

static BSON_INLINE int32_t
mcommon_atomic_int32_fetch_sub(int32_t volatile *val, int32_t v, enum mcommon_memory_order order)
{
   return _mcommon_emul_atomic_int32_fetch_add(val, -v, order);
}

static BSON_INLINE int32_t
mcommon_atomic_int32_exchange(int32_t volatile *val, int32_t v, enum mcommon_memory_order order)
{
   return _mcommon_emul_atomic_int32_exchange(val, v, order);
}

static BSON_INLINE int32_t
mcommon_atomic_int32_compare_exchange_strong(int32_t volatile *val,
                                             int32_t expect_value,
                                             int32_t new_value,
                                             enum mcommon_memory_order order)
{
   return _mcommon_emul_atomic_int32_compare_exchange_strong(val, expect_value, new_value, order);
}

static BSON_INLINE int32_t
mcommon_atomic_int32_compare_exchange_weak(int32_t volatile *val,
                                           int32_t expect_value,
                                           int32_t new_value,
                                           enum mcommon_memory_order order)
{
   return _mcommon_emul_atomic_int32_compare_exchange_weak(val, expect_value, new_value, order);
}
#endif /* MCOMMON_EMULATE_INT32 */

#if defined(MCOMMON_EMULATE_INT)
static BSON_INLINE int
mcommon_atomic_int_fetch(const int volatile *val, enum mcommon_memory_order order)
{
   return _mcommon_emul_atomic_int_fetch_add((int volatile *)val, 0, order);
}

static BSON_INLINE int
mcommon_atomic_int_fetch_add(int volatile *val, int v, enum mcommon_memory_order order)
{
   return _mcommon_emul_atomic_int_fetch_add(val, v, order);
}

static BSON_INLINE int
mcommon_atomic_int_fetch_sub(int volatile *val, int v, enum mcommon_memory_order order)
{
   return _mcommon_emul_atomic_int_fetch_add(val, -v, order);
}

static BSON_INLINE int
mcommon_atomic_int_exchange(int volatile *val, int v, enum mcommon_memory_order order)
{
   return _mcommon_emul_atomic_int_exchange(val, v, order);
}

static BSON_INLINE int
mcommon_atomic_int_compare_exchange_strong(int volatile *val,
                                           int expect_value,
                                           int new_value,
                                           enum mcommon_memory_order order)
{
   return _mcommon_emul_atomic_int_compare_exchange_strong(val, expect_value, new_value, order);
}

static BSON_INLINE int
mcommon_atomic_int_compare_exchange_weak(int volatile *val,
                                         int expect_value,
                                         int new_value,
                                         enum mcommon_memory_order order)
{
   return _mcommon_emul_atomic_int_compare_exchange_weak(val, expect_value, new_value, order);
}
#endif /* MCOMMON_EMULATE_INT */

static BSON_INLINE void *
mcommon_atomic_ptr_exchange(void *volatile *ptr, void *new_value, enum mcommon_memory_order ord)
{
#if defined(MCOMMON_EMULATE_PTR)
   return _mcommon_emul_atomic_ptr_exchange(ptr, new_value, ord);
#elif defined(MCOMMON_USE_LEGACY_GCC_ATOMICS)
   /* The older __sync_val_compare_and_swap also takes oldval */
   DEF_ATOMIC_OP(_InterlockedExchangePointer, , __sync_val_compare_and_swap, ord, ptr, *ptr, new_value);
#else
   DEF_ATOMIC_OP(_InterlockedExchangePointer, __atomic_exchange_n, , ord, ptr, new_value);
#endif
}

static BSON_INLINE void *
mcommon_atomic_ptr_compare_exchange_strong(void *volatile *ptr,
                                           void *expect,
                                           void *new_value,
                                           enum mcommon_memory_order ord)
{
   switch (ord) {
   case mcommon_memory_order_release:
   case mcommon_memory_order_acq_rel:
   case mcommon_memory_order_seq_cst:
      DEF_ATOMIC_CMPEXCH_STRONG(Pointer, , __ATOMIC_SEQ_CST, ptr, expect, new_value);
      return expect;
   case mcommon_memory_order_relaxed:
      DEF_ATOMIC_CMPEXCH_STRONG(Pointer, MSVC_MEMORDER_SUFFIX(_nf), __ATOMIC_RELAXED, ptr, expect, new_value);
      return expect;
   case mcommon_memory_order_consume:
      DEF_ATOMIC_CMPEXCH_STRONG(Pointer, MSVC_MEMORDER_SUFFIX(_acq), __ATOMIC_CONSUME, ptr, expect, new_value);
      return expect;
   case mcommon_memory_order_acquire:
      DEF_ATOMIC_CMPEXCH_STRONG(Pointer, MSVC_MEMORDER_SUFFIX(_acq), __ATOMIC_ACQUIRE, ptr, expect, new_value);
      return expect;
   default:
      BSON_UNREACHABLE("Invalid mcommon_memory_order value");
   }
}


static BSON_INLINE void *
mcommon_atomic_ptr_compare_exchange_weak(void *volatile *ptr,
                                         void *expect,
                                         void *new_value,
                                         enum mcommon_memory_order ord)
{
   switch (ord) {
   case mcommon_memory_order_release:
   case mcommon_memory_order_acq_rel:
   case mcommon_memory_order_seq_cst:
      DEF_ATOMIC_CMPEXCH_WEAK(Pointer, , __ATOMIC_SEQ_CST, ptr, expect, new_value);
      return expect;
   case mcommon_memory_order_relaxed:
      DEF_ATOMIC_CMPEXCH_WEAK(Pointer, MSVC_MEMORDER_SUFFIX(_nf), __ATOMIC_RELAXED, ptr, expect, new_value);
      return expect;
   case mcommon_memory_order_consume:
      DEF_ATOMIC_CMPEXCH_WEAK(Pointer, MSVC_MEMORDER_SUFFIX(_acq), __ATOMIC_CONSUME, ptr, expect, new_value);
      return expect;
   case mcommon_memory_order_acquire:
      DEF_ATOMIC_CMPEXCH_WEAK(Pointer, MSVC_MEMORDER_SUFFIX(_acq), __ATOMIC_ACQUIRE, ptr, expect, new_value);
      return expect;
   default:
      BSON_UNREACHABLE("Invalid mcommon_memory_order value");
   }
}


static BSON_INLINE void *
mcommon_atomic_ptr_fetch(void *volatile const *ptr, enum mcommon_memory_order ord)
{
   return mcommon_atomic_ptr_compare_exchange_strong((void *volatile *)ptr, NULL, NULL, ord);
}

#undef DECL_ATOMIC_STDINT
#undef DECL_ATOMIC_INTEGRAL
#undef DEF_ATOMIC_OP
#undef DEF_ATOMIC_CMPEXCH_STRONG
#undef DEF_ATOMIC_CMPEXCH_WEAK
#undef MSVC_MEMORDER_SUFFIX

/**
 * @brief Generate a full-fence memory barrier at the call site.
 */
static BSON_INLINE void
mcommon_atomic_thread_fence(void)
{
   BSON_IF_MSVC(MemoryBarrier();)
   BSON_IF_GNU_LIKE(__sync_synchronize();)
   MCOMMON_IF_GNU_LEGACY_ATOMICS(__sync_synchronize();)
}

#ifdef MCOMMON_USE_LEGACY_GCC_ATOMICS
#undef BSON_IF_GNU_LIKE
#define BSON_IF_GNU_LIKE(...) __VA_ARGS__
#endif
#undef MCOMMON_IF_GNU_LEGACY_ATOMICS
#undef MCOMMON_USE_LEGACY_GCC_ATOMICS


#undef MCOMMON_EMULATE_PTR
#undef MCOMMON_EMULATE_INT32
#undef MCOMMON_EMULATE_INT


#endif /* MONGO_C_DRIVER_COMMON_ATOMIC_PRIVATE_H */
