blob: fd0c121626b6551836241aac613c746373b2b3eb [file] [log] [blame]
use std::cell::Cell;
use std::time::{Duration, Instant as StdInstant};
/// When using a virtual clock, this defines how many nanoseconds we pretend are passing for each
/// basic block.
/// This number is pretty random, but it has been shown to approximately cause
/// some sample programs to run within an order of magnitude of real time on desktop CPUs.
/// (See `tests/pass/shims/time-with-isolation*.rs`.)
const NANOSECONDS_PER_BASIC_BLOCK: u64 = 5000;
#[derive(Debug)]
pub struct Instant {
kind: InstantKind,
}
#[derive(Debug)]
enum InstantKind {
Host(StdInstant),
Virtual { nanoseconds: u64 },
}
impl Instant {
pub fn checked_add(&self, duration: Duration) -> Option<Instant> {
match self.kind {
InstantKind::Host(instant) =>
instant.checked_add(duration).map(|i| Instant { kind: InstantKind::Host(i) }),
InstantKind::Virtual { nanoseconds } =>
u128::from(nanoseconds)
.checked_add(duration.as_nanos())
.and_then(|n| u64::try_from(n).ok())
.map(|nanoseconds| Instant { kind: InstantKind::Virtual { nanoseconds } }),
}
}
pub fn duration_since(&self, earlier: Instant) -> Duration {
match (&self.kind, earlier.kind) {
(InstantKind::Host(instant), InstantKind::Host(earlier)) =>
instant.duration_since(earlier),
(
InstantKind::Virtual { nanoseconds },
InstantKind::Virtual { nanoseconds: earlier },
) => Duration::from_nanos(nanoseconds.saturating_sub(earlier)),
_ => panic!("all `Instant` must be of the same kind"),
}
}
}
/// A monotone clock used for `Instant` simulation.
#[derive(Debug)]
pub struct Clock {
kind: ClockKind,
}
#[derive(Debug)]
enum ClockKind {
Host {
/// The "time anchor" for this machine's monotone clock.
time_anchor: StdInstant,
},
Virtual {
/// The "current virtual time".
nanoseconds: Cell<u64>,
},
}
impl Clock {
/// Create a new clock based on the availability of communication with the host.
pub fn new(communicate: bool) -> Self {
let kind = if communicate {
ClockKind::Host { time_anchor: StdInstant::now() }
} else {
ClockKind::Virtual { nanoseconds: 0.into() }
};
Self { kind }
}
/// Let the time pass for a small interval.
pub fn tick(&self) {
match &self.kind {
ClockKind::Host { .. } => {
// Time will pass without us doing anything.
}
ClockKind::Virtual { nanoseconds } => {
nanoseconds.update(|x| x + NANOSECONDS_PER_BASIC_BLOCK);
}
}
}
/// Sleep for the desired duration.
pub fn sleep(&self, duration: Duration) {
match &self.kind {
ClockKind::Host { .. } => std::thread::sleep(duration),
ClockKind::Virtual { nanoseconds } => {
// Just pretend that we have slept for some time.
let nanos: u64 = duration.as_nanos().try_into().unwrap();
nanoseconds.update(|x| x + nanos);
}
}
}
/// Return the `anchor` instant, to convert between monotone instants and durations relative to the anchor.
pub fn anchor(&self) -> Instant {
match &self.kind {
ClockKind::Host { time_anchor } => Instant { kind: InstantKind::Host(*time_anchor) },
ClockKind::Virtual { .. } => Instant { kind: InstantKind::Virtual { nanoseconds: 0 } },
}
}
pub fn now(&self) -> Instant {
match &self.kind {
ClockKind::Host { .. } => Instant { kind: InstantKind::Host(StdInstant::now()) },
ClockKind::Virtual { nanoseconds } =>
Instant { kind: InstantKind::Virtual { nanoseconds: nanoseconds.get() } },
}
}
}