use crate::core::clock::{ClockForest, DomainId, GlobalTime, OscillatorId, Rational};
use crate::core::error::{Error, Result};
use crate::core::state::{Sink, Source};
use crate::core::sync::{self, LockRank};
pub const DEFAULT_HZ: u64 = 1_000_000_000;
#[derive(Debug)]
struct Inner {
forest: ClockForest,
osc: OscillatorId,
domain: DomainId,
}
#[derive(Debug)]
pub struct GuestClock {
inner: sync::Mutex<Inner>,
}
impl GuestClock {
#[must_use]
pub fn new() -> GuestClock {
GuestClock::with_rate(Rational::integer(DEFAULT_HZ))
.expect("an integral frequency is always a valid oscillator")
}
pub fn with_rate(rate: Rational) -> Result<GuestClock> {
let mut forest = ClockForest::new();
let domain = forest
.add_oscillator("guest", rate)
.map_err(|e| clock_error(&e))?;
let osc = forest.root_of(domain).map_err(|e| clock_error(&e))?;
Ok(GuestClock {
inner: sync::Mutex::with_rank(
LockRank::SCHED,
Inner {
forest,
osc,
domain,
},
),
})
}
#[must_use]
pub fn ticks(&self) -> u64 {
let inner = self.inner.lock();
inner.forest.ticks(inner.domain).unwrap_or(0)
}
#[must_use]
pub fn now(&self) -> GlobalTime {
let inner = self.inner.lock();
inner
.forest
.global_time(inner.osc)
.unwrap_or(GlobalTime::ZERO)
}
#[must_use]
pub fn nanos(&self) -> u64 {
self.now().as_nanos()
}
#[must_use]
pub fn at_tick(&self, tick: u64) -> GlobalTime {
let inner = self.inner.lock();
inner
.forest
.global_time_of_tick(inner.domain, tick)
.unwrap_or(GlobalTime::ZERO)
}
#[must_use]
pub fn tick_of(&self, at: GlobalTime) -> u64 {
let inner = self.inner.lock();
inner.forest.units_at_global(inner.osc, at).unwrap_or(0)
}
pub fn advance(&self, ticks: u64) {
if ticks == 0 {
return;
}
let mut inner = self.inner.lock();
let domain = inner.domain;
let _ = inner.forest.advance_domain(domain, ticks);
}
pub fn advance_to(&self, at: GlobalTime) -> u64 {
let mut inner = self.inner.lock();
let osc = inner.osc;
inner.forest.advance_to_global(osc, at).unwrap_or(0)
}
pub fn save<S: Sink + ?Sized>(&self, sink: &mut S) -> Result<()> {
sink.write_u64(self.ticks())
}
pub fn load<'a, S: Source<'a> + ?Sized>(&self, source: &mut S) -> Result<()> {
let ticks = source.read_u64()?;
let mut inner = self.inner.lock();
let (osc, domain) = (inner.osc, inner.domain);
inner
.forest
.restore_unit_position(osc, ticks)
.map_err(|e| clock_error(&e))?;
inner
.forest
.restore_ticks(domain, ticks)
.map_err(|e| clock_error(&e))?;
Ok(())
}
}
impl Default for GuestClock {
fn default() -> Self {
GuestClock::new()
}
}
fn clock_error(e: &crate::core::clock::ClockError) -> Error {
Error::Config {
at: "guest clock".into(),
message: alloc::format!("{e}"),
}
}