use core::num::NonZeroU64;
use core::time::Duration;
#[inline]
pub(crate) fn pack_deadline(deadline_ns: u64) -> Option<NonZeroU64> {
NonZeroU64::new(deadline_ns)
}
#[inline]
pub(crate) fn deadline_at(now_ns: u64, ttl: Duration) -> u64 {
now_ns.saturating_add(ttl.as_nanos().min(u128::from(u64::MAX)) as u64)
}
#[inline]
pub(crate) fn remaining_ms(deadline: NonZeroU64, now_ns: u64) -> u64 {
deadline.get().saturating_sub(now_ns) / 1_000_000
}
#[cfg(not(any(feature = "external-clock", all(target_arch = "wasm32", target_os = "unknown"))))]
mod source {
use std::sync::OnceLock;
use std::time::Instant;
fn epoch() -> Instant {
static EPOCH: OnceLock<Instant> = OnceLock::new();
*EPOCH.get_or_init(Instant::now)
}
#[inline]
pub(crate) fn now_ns() -> u64 {
Instant::now().saturating_duration_since(epoch()).as_nanos() as u64
}
}
#[cfg(any(test, feature = "external-clock", all(target_arch = "wasm32", target_os = "unknown")))]
pub(crate) mod hostfed {
#[cfg(target_has_atomic = "64")]
use core::sync::atomic::AtomicU64;
use core::sync::atomic::{AtomicU32, Ordering};
#[cfg(target_has_atomic = "64")]
#[cfg_attr(test, allow(dead_code))]
pub(super) struct Cell(AtomicU64);
#[cfg(target_has_atomic = "64")]
#[cfg_attr(test, allow(dead_code))]
impl Cell {
pub(super) const fn new() -> Self {
Cell(AtomicU64::new(0))
}
#[inline]
pub(super) fn load(&self) -> u64 {
self.0.load(Ordering::Relaxed)
}
#[inline]
pub(super) fn store(&self, v: u64) {
self.0.store(v, Ordering::Relaxed);
}
}
#[cfg(not(target_has_atomic = "64"))]
pub(super) type Cell = SeqCell;
#[allow(dead_code)]
pub(crate) struct SeqCell {
seq: AtomicU32,
hi: AtomicU32,
lo: AtomicU32,
}
#[allow(dead_code)]
impl SeqCell {
pub(crate) const fn new() -> Self {
SeqCell { seq: AtomicU32::new(0), hi: AtomicU32::new(0), lo: AtomicU32::new(0) }
}
#[inline]
pub(crate) fn load(&self) -> u64 {
loop {
let s1 = self.seq.load(Ordering::Acquire);
let hi = self.hi.load(Ordering::Acquire);
let lo = self.lo.load(Ordering::Acquire);
let s2 = self.seq.load(Ordering::Acquire);
if s1 == s2 && s1 & 1 == 0 {
return (u64::from(hi) << 32) | u64::from(lo);
}
}
}
#[inline]
pub(crate) fn store(&self, v: u64) {
let s = self.seq.load(Ordering::Relaxed);
self.seq.store(s.wrapping_add(1), Ordering::Release); self.hi.store((v >> 32) as u32, Ordering::Release);
self.lo.store(v as u32, Ordering::Release);
self.seq.store(s.wrapping_add(2), Ordering::Release); }
}
}
#[cfg(any(feature = "external-clock", all(target_arch = "wasm32", target_os = "unknown")))]
mod source {
static MONO_NS: super::hostfed::Cell = super::hostfed::Cell::new();
#[inline]
pub(crate) fn now_ns() -> u64 {
MONO_NS.load()
}
#[inline]
pub fn set_clock_ns(ns: u64) {
MONO_NS.store(ns);
}
}
#[cfg(any(feature = "external-clock", all(target_arch = "wasm32", target_os = "unknown")))]
mod wall {
static WALL_MS: super::hostfed::Cell = super::hostfed::Cell::new();
#[inline]
pub(crate) fn now_unix_ms() -> u64 {
WALL_MS.load()
}
#[inline]
pub fn set_wall_clock_ms(ms: u64) {
WALL_MS.store(ms);
}
}
pub(crate) use source::now_ns;
#[cfg(any(feature = "external-clock", all(target_arch = "wasm32", target_os = "unknown")))]
pub use source::set_clock_ns;
#[cfg(any(feature = "external-clock", all(target_arch = "wasm32", target_os = "unknown")))]
pub(crate) use wall::now_unix_ms as wall_now_unix_ms;
#[cfg(any(feature = "external-clock", all(target_arch = "wasm32", target_os = "unknown")))]
pub use wall::set_wall_clock_ms;
#[cfg(test)]
mod seqlock_tests {
#[test]
fn hostfed_cell_never_tears_under_a_writer() {
use super::hostfed::SeqCell as Cell;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
let cell = Arc::new(Cell::new());
let stop = Arc::new(AtomicBool::new(false));
let writer = {
let (cell, stop) = (Arc::clone(&cell), Arc::clone(&stop));
std::thread::spawn(move || {
let mut i: u32 = 1;
while !stop.load(Ordering::Relaxed) {
let v = (u64::from(i) << 32) | u64::from(!i);
cell.store(v);
i = i.wrapping_add(1);
}
})
};
let readers: Vec<_> = (0..3)
.map(|_| {
let (cell, stop) = (Arc::clone(&cell), Arc::clone(&stop));
std::thread::spawn(move || {
let mut seen = 0u64;
while !stop.load(Ordering::Relaxed) {
let v = cell.load();
if v != 0 {
let (hi, lo) = ((v >> 32) as u32, v as u32);
assert_eq!(hi, !lo, "torn read: {v:#x}");
seen += 1;
}
}
seen
})
})
.collect();
std::thread::sleep(std::time::Duration::from_millis(300));
stop.store(true, Ordering::Relaxed);
writer.join().unwrap();
let total: u64 = readers.into_iter().map(|r| r.join().unwrap()).sum();
assert!(total > 10_000, "readers barely ran ({total} loads)");
}
}