use core::{
num::NonZeroUsize,
sync::atomic::{AtomicU32, AtomicU8, AtomicUsize, Ordering},
time::Duration,
};
use std::thread::available_parallelism;
#[inline(always)]
pub fn sleep(dur: Duration) {
std::thread::sleep(dur)
}
#[allow(dead_code)]
#[inline(always)]
pub fn spin_hint() {
std::hint::spin_loop()
}
#[allow(dead_code)]
#[inline(always)]
pub fn yield_now_std() {
std::thread::yield_now();
}
#[allow(dead_code)]
#[inline(always)]
pub fn spin_wait(count: usize) {
for _ in 0..count {
spin_hint();
}
}
#[allow(dead_code)]
#[inline(always)]
pub fn yield_now() {
const OFFSET: usize = 1 << 6;
spin_wait((random_u7() as usize).wrapping_add(OFFSET));
}
#[allow(dead_code)]
#[inline(always)]
fn random_u7() -> u8 {
static SEED: AtomicU8 = AtomicU8::new(13);
const MULTIPLIER: u8 = 113;
let seed = SEED.fetch_add(1, Ordering::Relaxed);
seed.wrapping_mul(MULTIPLIER) & 0x7F
}
#[allow(dead_code)]
#[inline(always)]
fn random_u32() -> u32 {
static SEED: AtomicU32 = AtomicU32::new(13);
const MULTIPLIER: u32 = 1812433253;
let seed = SEED.fetch_add(1, Ordering::Relaxed);
seed.wrapping_mul(MULTIPLIER)
}
#[allow(dead_code)]
#[inline(always)]
pub fn randomize(d: usize) -> usize {
d - (d >> 3) + random_u32() as usize % (d >> 2)
}
static PARALLELISM: AtomicUsize = AtomicUsize::new(0);
#[inline(always)]
pub fn get_parallelism() -> usize {
let mut p = PARALLELISM.load(Ordering::Relaxed);
if p == 0 {
p = usize::from(available_parallelism().unwrap_or(NonZeroUsize::new(1).unwrap()));
PARALLELISM.store(p, Ordering::SeqCst);
}
p
}
#[allow(dead_code)]
#[allow(clippy::reversed_empty_ranges)]
#[inline(always)]
pub fn spin_cond<F: Fn() -> bool>(cond: F) {
if get_parallelism() == 1 {
while !cond() {
yield_now_std();
}
return;
}
const NO_YIELD: usize = 1;
const SPIN_YIELD: usize = 1;
const OS_YIELD: usize = 0;
const ZERO_SLEEP: usize = 2;
const SPINS: u32 = 8;
let mut spins: u32 = SPINS;
for _ in 0..NO_YIELD {
for _ in 0..SPINS / 2 {
if cond() {
return;
}
spin_hint();
}
}
loop {
for _ in 0..SPIN_YIELD {
yield_now();
for _ in 0..spins {
if cond() {
return;
}
}
}
for _ in 0..OS_YIELD {
yield_now_std();
for _ in 0..spins {
if cond() {
return;
}
}
}
for _ in 0..ZERO_SLEEP {
sleep(Duration::from_nanos(0));
for _ in 0..spins {
if cond() {
return;
}
}
}
if spins < (1 << 30) {
spins <<= 1;
}
sleep(Duration::from_nanos(1 << 20));
}
}