use std::sync::{
atomic::{AtomicU64, Ordering},
Arc,
};
#[derive(Clone)]
pub struct ClockSource {
inner: ClockInner,
}
#[derive(Clone)]
enum ClockInner {
System,
Manual(Arc<AtomicU64>),
}
impl ClockSource {
pub fn system() -> Self {
ClockSource {
inner: ClockInner::System,
}
}
pub fn manual(start: u64) -> Self {
ClockSource {
inner: ClockInner::Manual(Arc::new(AtomicU64::new(start))),
}
}
pub fn now(&self) -> u64 {
match &self.inner {
ClockInner::System => std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_secs())
.unwrap_or(0),
ClockInner::Manual(t) => t.load(Ordering::Relaxed),
}
}
pub fn advance(&self, secs: u64) {
if let ClockInner::Manual(t) = &self.inner {
t.fetch_add(secs, Ordering::Relaxed);
}
}
pub fn set(&self, secs: u64) {
if let ClockInner::Manual(t) = &self.inner {
t.store(secs, Ordering::Relaxed);
}
}
}
impl Default for ClockSource {
fn default() -> Self {
ClockSource::system()
}
}
impl std::fmt::Debug for ClockSource {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self.inner {
ClockInner::System => f.write_str("ClockSource::System"),
ClockInner::Manual(t) => {
write!(f, "ClockSource::Manual({})", t.load(Ordering::Relaxed))
}
}
}
}
pub const DEFAULT_REGISTRY_CAPACITY: usize = 4096;
#[derive(Clone, Debug)]
pub struct SelectorConfig {
pub clock: ClockSource,
pub rng_seed: Option<u64>,
pub registry_capacity: usize,
}
impl Default for SelectorConfig {
fn default() -> Self {
SelectorConfig {
clock: ClockSource::system(),
rng_seed: None,
registry_capacity: DEFAULT_REGISTRY_CAPACITY,
}
}
}
impl SelectorConfig {
pub fn deterministic(start: u64, seed: u64) -> Self {
SelectorConfig {
clock: ClockSource::manual(start),
rng_seed: Some(seed),
registry_capacity: DEFAULT_REGISTRY_CAPACITY,
}
}
pub(crate) fn effective_seed(&self) -> u64 {
self.rng_seed.unwrap_or(0x5EED_D16C_0DE0_u64)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn manual_clock_advances_and_sets() {
let c = ClockSource::manual(100);
assert_eq!(c.now(), 100);
c.advance(50);
assert_eq!(c.now(), 150);
c.set(10);
assert_eq!(c.now(), 10);
}
#[test]
fn system_clock_is_nonzero_and_ignores_manual_ops() {
let c = ClockSource::system();
assert!(c.now() > 1_600_000_000); c.advance(1000); c.set(0); assert!(c.now() > 1_600_000_000);
}
#[test]
fn default_config_uses_system_clock_and_default_capacity() {
let cfg = SelectorConfig::default();
assert_eq!(cfg.registry_capacity, DEFAULT_REGISTRY_CAPACITY);
assert!(cfg.rng_seed.is_none());
assert_eq!(
cfg.effective_seed(),
super::SelectorConfig::default().effective_seed()
);
assert_ne!(
cfg.effective_seed(),
0,
"the fixed default seed is deterministic + non-zero"
);
}
#[test]
fn deterministic_config_is_reproducible() {
let a = SelectorConfig::deterministic(1000, 42);
let b = SelectorConfig::deterministic(1000, 42);
assert_eq!(a.clock.now(), b.clock.now());
assert_eq!(a.effective_seed(), b.effective_seed());
assert_eq!(a.effective_seed(), 42);
}
#[test]
fn clock_debug_renders() {
assert!(format!("{:?}", ClockSource::system()).contains("System"));
assert!(format!("{:?}", ClockSource::manual(7)).contains('7'));
}
}