use std::time::{Duration, Instant, SystemTime};
use crate::walltime::WallTime;
pub fn now_wall() -> WallTime {
SystemTime::now().into()
}
pub fn now_mono() -> Instant {
Instant::now()
}
pub fn deadline_from_now(d: Duration) -> Instant {
Instant::now() + d
}
pub fn thread_sleep_quantum() -> f64 {
#[cfg(unix)]
{
let hz = unsafe { libc::sysconf(libc::_SC_CLK_TCK) } as f64;
if hz <= 0.0 { 0.0 } else { 1.0 / hz }
}
#[cfg(not(unix))]
{
0.01
}
}
pub fn quantize_to_sleep_quantum(seconds: f64) -> f64 {
let quantum = thread_sleep_quantum();
if quantum <= 0.0 {
return seconds;
}
let ticks = seconds / quantum;
let rounded = if ticks > 0.0 {
ticks + 0.5
} else {
ticks - 0.5
};
quantum * c_long_cast(rounded) as f64
}
fn c_long_cast(f: f64) -> i64 {
const MIN: f64 = -9_223_372_036_854_775_808.0; const LIMIT: f64 = 9_223_372_036_854_775_808.0; if f.is_nan() || f < MIN || f >= LIMIT {
i64::MIN
} else {
f as i64
}
}
#[cfg(test)]
mod tests {
use super::*;
fn production_scope(src: &str) -> &str {
match src.find("\n#[cfg(test)]") {
Some(i) => &src[..i],
None => src,
}
}
#[test]
fn the_tick_rate_is_asked_for_not_restated() {
let src: String = production_scope(include_str!("time.rs"))
.lines()
.filter(|l| !l.trim_start().starts_with("//"))
.collect::<Vec<_>>()
.join("\n");
let src = src.as_str();
assert!(
src.contains("libc::sysconf(libc::_SC_CLK_TCK)"),
"thread_sleep_quantum must ask the OS for the tick rate"
);
assert!(
!src.contains("#[cfg(target_os = \"linux\")]"),
"the tick-rate arm must select on `unix`, not on `linux`: RTEMS is \
a unix that answers _SC_CLK_TCK from the boot crate's define"
);
assert_eq!(
src.matches("#[cfg(unix)]").count(),
1,
"exactly one arm asks; if a second appears, this guard needs updating"
);
assert_eq!(
src.matches("0.01").count(),
1,
"0.01 may appear only once, in the non-unix arm"
);
for restatement in ["10000", "10_000", "0.010", "1e-2"] {
assert!(
!src.contains(restatement),
"`{restatement}` restates CONFIGURE_MICROSECONDS_PER_TICK; \
read it back through sysconf instead"
);
}
}
#[test]
fn test_now_wall() {
let t = now_wall();
assert!(t.since_unix_epoch().as_secs() > 0);
}
#[test]
fn test_now_mono() {
let t1 = now_mono();
let t2 = now_mono();
assert!(t2 >= t1);
}
#[test]
fn test_deadline_from_now() {
let before = Instant::now();
let deadline = deadline_from_now(Duration::from_secs(10));
assert!(deadline > before);
assert!(deadline <= before + Duration::from_secs(11));
}
#[test]
fn c_long_cast_matches_cvttsd2si() {
assert_eq!(c_long_cast(0.0), 0);
assert_eq!(c_long_cast(-0.0), 0);
assert_eq!(c_long_cast(0.5), 0);
assert_eq!(c_long_cast(-0.5), 0); assert_eq!(c_long_cast(1.9), 1);
assert_eq!(c_long_cast(-1.9), -1);
assert_eq!(c_long_cast(-9_223_372_036_854_775_808.0), i64::MIN);
assert_eq!(c_long_cast(9_223_372_036_854_775_808.0), i64::MIN); assert_eq!(c_long_cast(1e300), i64::MIN);
assert_eq!(c_long_cast(-1e300), i64::MIN);
assert_eq!(c_long_cast(f64::INFINITY), i64::MIN);
assert_eq!(c_long_cast(f64::NEG_INFINITY), i64::MIN);
assert_eq!(c_long_cast(f64::NAN), i64::MIN);
}
#[test]
fn quantize_dly_boundaries_match_c() {
let q = thread_sleep_quantum();
assert!(q > 0.0, "test assumes a positive clock quantum, got {q}");
let zero = quantize_to_sleep_quantum(0.0);
assert_eq!(zero, 0.0);
assert!(
zero.is_sign_positive(),
"DLY=0.0 must round to +0.0 (renders \"0\"), got a negative zero"
);
let neg_zero = quantize_to_sleep_quantum(-0.0);
assert_eq!(neg_zero, 0.0);
assert!(
neg_zero.is_sign_positive(),
"DLY=-0.0 must round to +0.0, got a negative zero"
);
let tiny = quantize_to_sleep_quantum(q / 4.0);
assert_eq!(tiny, 0.0);
assert!(tiny.is_sign_positive(), "tiny +dly rounds to +0.0");
let tiny_neg = quantize_to_sleep_quantum(-q / 4.0);
assert_eq!(tiny_neg, 0.0);
assert!(
tiny_neg.is_sign_positive(),
"a -dly rounding to zero must serve +0.0, not -0.0"
);
assert_eq!(quantize_to_sleep_quantum(3.0 * q), 3.0 * q);
assert_eq!(quantize_to_sleep_quantum(-3.0 * q), -3.0 * q);
assert_eq!(quantize_to_sleep_quantum(1.5 * q), 2.0 * q);
assert_eq!(quantize_to_sleep_quantum(-1.5 * q), -2.0 * q);
let huge = quantize_to_sleep_quantum(1e300);
assert_eq!(huge, q * (i64::MIN as f64));
assert!(
huge.is_finite() && huge < 0.0,
"a huge +dly must serve C's ~-9.22e16, not inf, got {huge}"
);
let huge_neg = quantize_to_sleep_quantum(-1e300);
assert_eq!(huge_neg, q * (i64::MIN as f64));
assert_eq!(
quantize_to_sleep_quantum(f64::INFINITY),
q * (i64::MIN as f64)
);
assert_eq!(
quantize_to_sleep_quantum(f64::NEG_INFINITY),
q * (i64::MIN as f64)
);
}
}