1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
|
#ifndef FASTX_PARSER_THREAD_UTILS_HPP
#define FASTX_PARSER_THREAD_UTILS_HPP
#include <cassert>
#include <thread>
#include <chrono>
#include <random>
#include <pthread.h>
// Most of this code is taken directly from https://github.com/geidav/spinlocks-bench/blob/master/os.hpp.
// However, things may be renamed, modified, or randomly mangled over time.
#define ALWAYS_INLINE inline __attribute__((__always_inline__))
namespace fastx_parser {
namespace thread_utils {
static const constexpr size_t MIN_BACKOFF_ITERS = 32;
static const size_t MAX_BACKOFF_ITERS = 1024;
ALWAYS_INLINE static void cpuRelax() {
#if defined(__aarch64__) || defined(arm64)
asm volatile("yield" ::: "memory");
#elif defined(__PPC64__) || defined(PPC64) || defined(__ppc64__)
asm("ori 0,0,0");
#else
asm("pause");
#endif
}
ALWAYS_INLINE void yieldSleep() {
using namespace std::chrono;
std::chrono::microseconds ytime(500);
std::this_thread::sleep_for(ytime);
}
ALWAYS_INLINE void backoffExp(size_t &curMaxIters) {
thread_local std::uniform_int_distribution<size_t> dist;
thread_local std::minstd_rand gen(std::random_device{}());
const size_t spinIters = dist(gen, decltype(dist)::param_type{0, curMaxIters});
curMaxIters = std::min(2*curMaxIters, MAX_BACKOFF_ITERS);
for (size_t i=0; i<spinIters; i++) { cpuRelax(); }
}
ALWAYS_INLINE void backoffOrYield(size_t& curMaxDelay) {
if (curMaxDelay >= MAX_BACKOFF_ITERS) {
yieldSleep();
curMaxDelay = MIN_BACKOFF_ITERS;
}
backoffExp(curMaxDelay);
}
}
}
#endif // FASTX_PARSER_THREAD_UTILS_HPP
|