miri/clock.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
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: u128 = 5000;
#[derive(Debug)]
pub struct Instant {
kind: InstantKind,
}
#[derive(Debug)]
enum InstantKind {
Host(StdInstant),
Virtual { nanoseconds: u128 },
}
impl Instant {
/// Will try to add `duration`, but if that overflows it may add less.
pub fn add_lossy(&self, duration: Duration) -> Instant {
match self.kind {
InstantKind::Host(instant) => {
// If this overflows, try adding just 1h and assume that will not overflow.
let i = instant
.checked_add(duration)
.unwrap_or_else(|| instant.checked_add(Duration::from_secs(3600)).unwrap());
Instant { kind: InstantKind::Host(i) }
}
InstantKind::Virtual { nanoseconds } => {
let n = nanoseconds.saturating_add(duration.as_nanos());
Instant { kind: InstantKind::Virtual { nanoseconds: n } }
}
}
}
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 },
) => {
let duration = nanoseconds.saturating_sub(earlier);
// `Duration` does not provide a nice constructor from a `u128` of nanoseconds,
// so we have to implement this ourselves.
// It is possible for second to overflow because u64::MAX < (u128::MAX / 1e9).
// It will be saturated to u64::MAX seconds if the value after division exceeds u64::MAX.
let seconds = u64::try_from(duration / 1_000_000_000).unwrap_or(u64::MAX);
// It is impossible for nanosecond to overflow because u32::MAX > 1e9.
let nanosecond = u32::try_from(duration.wrapping_rem(1_000_000_000)).unwrap();
Duration::new(seconds, nanosecond)
}
_ => 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 "epoch" for this machine's monotone clock:
/// the moment we consider to be time = 0.
epoch: StdInstant,
},
Virtual {
/// The "current virtual time".
nanoseconds: Cell<u128>,
},
}
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 { epoch: 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: u128 = duration.as_nanos();
nanoseconds.update(|x| {
x.checked_add(nanos)
.expect("Miri's virtual clock cannot represent an execution this long")
});
}
}
}
/// Return the `epoch` instant (time = 0), to convert between monotone instants and absolute durations.
pub fn epoch(&self) -> Instant {
match &self.kind {
ClockKind::Host { epoch } => Instant { kind: InstantKind::Host(*epoch) },
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() } },
}
}
}