use std::sync::OnceLock;
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::{Duration, Instant};
static BUSY_AT: [Slot; 64] = [const { Slot(AtomicU64::new(0)) }; 64];
#[repr(align(128))]
struct Slot(AtomicU64);
impl Slot {
fn touch(&self, now: u64) {
if now.saturating_sub(self.0.load(Ordering::Relaxed)) >= BUSY_WINDOW_NS / 4 {
self.0.store(now, Ordering::Relaxed);
}
}
}
const BUSY_WINDOW_NS: u64 = 2_000_000;
pub(crate) fn clock_ns() -> u64 {
static START: OnceLock<Instant> = OnceLock::new();
(START.get_or_init(Instant::now).elapsed().as_nanos() as u64).max(1)
}
pub fn mark_busy(shard: u32) {
BUSY_AT[shard as usize % 64].touch(clock_ns());
}
pub fn few_busy() -> bool {
if spin_off() {
return false;
}
let now = clock_ns();
let mut busy = 0;
for at in &BUSY_AT {
let t = at.0.load(Ordering::Relaxed);
if t != 0 && now.saturating_sub(t) <= BUSY_WINDOW_NS {
busy += 1;
if busy > 2 {
return false;
}
}
}
true
}
static HOST_BUSY_AT: [Slot; 64] = [const { Slot(AtomicU64::new(0)) }; 64];
pub fn mark_host_busy() {
use std::sync::atomic::AtomicUsize;
static NEXT: AtomicUsize = AtomicUsize::new(0);
thread_local! {
static SLOT: usize = NEXT.fetch_add(1, Ordering::Relaxed) % 64;
}
let i = SLOT.with(|s| *s);
HOST_BUSY_AT[i].touch(clock_ns());
}
pub fn spare_core() -> bool {
static CORES: OnceLock<usize> = OnceLock::new();
let cores = *CORES.get_or_init(|| std::thread::available_parallelism().map_or(1, |n| n.get()));
if cores <= 2 || spin_off() {
return false;
}
let now = clock_ns();
let recent = |at: &Slot| {
let t = at.0.load(Ordering::Relaxed);
t != 0 && now.saturating_sub(t) <= BUSY_WINDOW_NS
};
let mut running = 1;
for at in BUSY_AT.iter().chain(HOST_BUSY_AT.iter()) {
if recent(at) {
running += 1;
if running >= cores {
return false;
}
}
}
true
}
pub fn may_spin() -> bool {
static SPARE: OnceLock<bool> = OnceLock::new();
*SPARE.get_or_init(|| {
!spin_off() && std::thread::available_parallelism().is_ok_and(|n| n.get() > 2)
})
}
pub fn spin_off() -> bool {
static OFF: OnceLock<bool> = OnceLock::new();
*OFF.get_or_init(|| std::env::var("FUSEE_SPIN").is_ok_and(|v| v.trim() == "0"))
}
#[derive(Debug)]
pub struct Budget {
recent_ns: AtomicU64,
cap_ns: u64,
}
impl Budget {
pub const fn new(cap: Duration) -> Self {
Self {
recent_ns: AtomicU64::new(0),
cap_ns: cap.as_nanos() as u64,
}
}
pub fn get(&self) -> Duration {
let both = self.recent_ns.load(Ordering::Relaxed);
let (older, last) = (both >> 32, both & u64::from(u32::MAX));
let shorter = match older {
0 => last,
_ => older.min(last),
};
if shorter == 0 || shorter > self.cap_ns || !may_spin() {
return Duration::ZERO;
}
Duration::from_nanos((older.max(last) * 2).min(self.cap_ns))
}
pub fn record(&self, waited: Duration) {
let ns = u64::try_from(waited.as_nanos())
.unwrap_or(u64::MAX)
.clamp(1, u64::from(u32::MAX));
let last = self.recent_ns.load(Ordering::Relaxed) & u64::from(u32::MAX);
self.recent_ns.store(last << 32 | ns, Ordering::Relaxed);
}
}
pub fn spin_until(budget: Duration, mut ready: impl FnMut() -> bool) -> bool {
if ready() {
return true;
}
if budget.is_zero() || spin_off() {
return false;
}
let until = Instant::now() + budget;
loop {
for _ in 0..32 {
std::hint::spin_loop();
if ready() {
return true;
}
}
if Instant::now() >= until {
return false;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_budget_follows_the_last_two_waits() {
let b = Budget::new(Duration::from_micros(50));
assert_eq!(b.get(), Duration::ZERO, "no wait seen yet");
let us = |n: u64| {
if may_spin() {
Duration::from_micros(n)
} else {
Duration::ZERO
}
};
b.record(Duration::from_micros(10));
assert_eq!(b.get(), us(20), "twice the last wait");
b.record(Duration::from_micros(20));
assert_eq!(b.get(), us(40), "twice the longer of the last two");
b.record(Duration::from_micros(5));
assert_eq!(b.get(), us(40), "twice the longer of the last two");
b.record(Duration::from_micros(30));
assert_eq!(b.get(), us(50), "at most the cap");
b.record(Duration::from_millis(5));
assert_eq!(b.get(), us(50), "one long wait does not stop it");
b.record(Duration::from_secs(10));
assert_eq!(b.get(), Duration::ZERO, "two long waits stop it");
}
#[test]
fn spinning_stops_when_ready_or_out_of_budget() {
let mut n = 0;
assert!(spin_until(Duration::from_millis(100), || {
n += 1;
n == 100
}));
let t = Instant::now();
assert!(!spin_until(Duration::from_micros(200), || false));
assert!(t.elapsed() >= Duration::from_micros(200));
assert!(!spin_until(Duration::ZERO, || false));
}
}