
#ifndef PLS_SYSTEM_DETAILS_H
#define PLS_SYSTEM_DETAILS_H

#include <cstdint>
#if (COMPILER == MVCC)
#include <emmintrin.h>
#endif

namespace pls {
namespace internal {
namespace base {

/**
 * Collection of system details, e.g. hardware cache line size.
 *
 * PORTABILITY:
 * Currently sane default values for x86.
 */
namespace system_details {

/**
 * Pointer Types needed for ABA protection mixed into addresses.
 * pointer_t should be an integer type capable of holding ANY pointer value.
 */
using pointer_t = std::uintptr_t;
constexpr pointer_t ZERO_POINTER = 0;
constexpr pointer_t MAX_POINTER = ~ZERO_POINTER;

/**
 * Biggest type that supports atomic CAS operations.
 * Usually it is sane to assume a pointer can be swapped in a single CAS operation.
 */
using cas_integer = pointer_t;
constexpr cas_integer MIN_CAS_INTEGER = 0;
constexpr cas_integer MAX_CAS_INTEGER = ~MIN_CAS_INTEGER;
constexpr cas_integer FIRST_HALF_CAS_INTEGER = MAX_CAS_INTEGER << ((sizeof(cas_integer) / 2) * 8);
constexpr cas_integer SECOND_HALF_CAS_INTEGER = ~FIRST_HALF_CAS_INTEGER;

/**
 * Most processors have 64 byte cache lines (last 6 bit of the address are zero at line beginnings).
 */
constexpr unsigned int CACHE_LINE_ADDRESS_BITS = 6;
constexpr pointer_t CACHE_LINE_SIZE = 2u << (CACHE_LINE_ADDRESS_BITS - 1);
constexpr pointer_t CACHE_LINE_ADDRESS_USED_BITS = MAX_POINTER << CACHE_LINE_ADDRESS_BITS;
constexpr pointer_t CACHE_LINE_ADDRESS_UNUSED_BITS = ~CACHE_LINE_ADDRESS_USED_BITS;

/**
 * Choose one of the following ways to store thread specific data.
 * Try to choose the fastest available on this processor/system.
 */
//#define PLS_THREAD_SPECIFIC_PTHREAD
#define PLS_THREAD_SPECIFIC_COMPILER

/**
 * When spinning one wants to 'relax' the CPU from some task,
 * e.g. disabling speculative execution/branch prediction
 * or reducing its clock speed.
 * This is both good for power draw, as well as for hyperthreading.
 *
 * Choose the implementation appropriate for your compiler-cpu combination.
 */
#if (COMPILER == MVCC)
inline void relax_cpu() {
  _mm_pause();
}
#elif (COMPILER == GCC || COMPILER == LLVM)
inline void relax_cpu() {
  asm("pause");
}
#endif

}
}
}
}

#endif //PLS_SYSTEM_DETAILS_H
