Skip to main content

miri/
clock.rs

1use std::cell::Cell;
2use std::time::{Duration, Instant as StdInstant, SystemTime};
3
4use crate::MiriMachine;
5
6/// When using a virtual clock, this defines how many nanoseconds we pretend are passing for each
7/// basic block.
8/// This number is pretty random, but it has been shown to approximately cause
9/// some sample programs to run within an order of magnitude of real time on desktop CPUs.
10/// (See `tests/pass/shims/time-with-isolation*.rs`.)
11const NANOSECONDS_PER_BASIC_BLOCK: u128 = 5000;
12
13/// An instant (a fixed moment in time) in Miri's monotone clock.
14#[derive(Debug)]
15pub struct Instant {
16    kind: InstantKind,
17}
18
19#[derive(Debug)]
20enum InstantKind {
21    Host(StdInstant),
22    Virtual { nanoseconds: u128 },
23}
24
25impl Instant {
26    /// Will try to add `duration`, but if that overflows it may add less.
27    pub fn add_lossy(&self, duration: Duration) -> Instant {
28        match self.kind {
29            InstantKind::Host(instant) => {
30                // If this overflows, try adding just 1h and assume that will not overflow.
31                let i = instant
32                    .checked_add(duration)
33                    .unwrap_or_else(|| instant.checked_add(Duration::from_secs(3600)).unwrap());
34                Instant { kind: InstantKind::Host(i) }
35            }
36            InstantKind::Virtual { nanoseconds } => {
37                let n = nanoseconds.saturating_add(duration.as_nanos());
38                Instant { kind: InstantKind::Virtual { nanoseconds: n } }
39            }
40        }
41    }
42
43    pub fn duration_since(&self, earlier: Instant) -> Duration {
44        match (&self.kind, earlier.kind) {
45            (InstantKind::Host(instant), InstantKind::Host(earlier)) =>
46                instant.duration_since(earlier),
47            (
48                InstantKind::Virtual { nanoseconds },
49                InstantKind::Virtual { nanoseconds: earlier },
50            ) => {
51                let duration = nanoseconds.saturating_sub(earlier);
52                Duration::from_nanos_u128(duration)
53            }
54            _ => panic!("all `Instant` must be of the same kind"),
55        }
56    }
57}
58
59/// A monotone clock used for `Instant` simulation.
60#[derive(Debug)]
61pub struct MonotonicClock {
62    kind: MonotonicClockKind,
63}
64
65#[derive(Debug)]
66enum MonotonicClockKind {
67    Host {
68        /// The "epoch" for this machine's monotone clock:
69        /// the moment we consider to be time = 0.
70        epoch: StdInstant,
71    },
72    Virtual {
73        /// The "current virtual time".
74        nanoseconds: Cell<u128>,
75    },
76}
77
78impl MonotonicClock {
79    /// Create a new clock based on the availability of communication with the host.
80    pub fn new(communicate: bool) -> Self {
81        let kind = if communicate {
82            MonotonicClockKind::Host { epoch: StdInstant::now() }
83        } else {
84            MonotonicClockKind::Virtual { nanoseconds: 0.into() }
85        };
86
87        Self { kind }
88    }
89
90    /// Let the time pass for a small interval.
91    pub fn tick(&self) {
92        match &self.kind {
93            MonotonicClockKind::Host { .. } => {
94                // Time will pass without us doing anything.
95            }
96            MonotonicClockKind::Virtual { nanoseconds } => {
97                nanoseconds.update(|x| x + NANOSECONDS_PER_BASIC_BLOCK);
98            }
99        }
100    }
101
102    /// Sleep for the desired duration.
103    pub fn sleep(&self, duration: Duration) {
104        match &self.kind {
105            MonotonicClockKind::Host { .. } => std::thread::sleep(duration),
106            MonotonicClockKind::Virtual { nanoseconds } => {
107                // Just pretend that we have slept for some time.
108                let nanos: u128 = duration.as_nanos();
109                nanoseconds.update(|x| {
110                    x.checked_add(nanos)
111                        .expect("Miri's virtual clock cannot represent an execution this long")
112                });
113            }
114        }
115    }
116
117    /// Return the `epoch` instant (time = 0), to convert between monotone instants and absolute durations.
118    pub fn epoch(&self) -> Instant {
119        match &self.kind {
120            MonotonicClockKind::Host { epoch } => Instant { kind: InstantKind::Host(*epoch) },
121            MonotonicClockKind::Virtual { .. } =>
122                Instant { kind: InstantKind::Virtual { nanoseconds: 0 } },
123        }
124    }
125
126    pub fn now(&self) -> Instant {
127        match &self.kind {
128            MonotonicClockKind::Host { .. } =>
129                Instant { kind: InstantKind::Host(StdInstant::now()) },
130            MonotonicClockKind::Virtual { nanoseconds } =>
131                Instant { kind: InstantKind::Virtual { nanoseconds: nanoseconds.get() } },
132        }
133    }
134}
135
136/// A deadline for some event to occur.
137#[derive(Debug)]
138pub enum Deadline {
139    Monotonic(Instant),
140    RealTime(SystemTime),
141}
142
143impl From<Instant> for Deadline {
144    fn from(value: Instant) -> Self {
145        Deadline::Monotonic(value)
146    }
147}
148
149impl Deadline {
150    /// Will try to add `duration`, but if that overflows it may add less.
151    fn add_lossy(&self, duration: Duration) -> Self {
152        match self {
153            Deadline::Monotonic(i) => Deadline::Monotonic(i.add_lossy(duration)),
154            Deadline::RealTime(s) => {
155                // If this overflows, try adding just 1h and assume that will not overflow.
156                Deadline::RealTime(
157                    s.checked_add(duration)
158                        .unwrap_or_else(|| s.checked_add(Duration::from_secs(3600)).unwrap()),
159                )
160            }
161        }
162    }
163}
164
165/// The clock to use for the timeout you are asking for.
166#[derive(Debug, Copy, Clone, PartialEq)]
167pub enum TimeoutClock {
168    /// The timeout is measured using the monotone clock.
169    Monotonic,
170    /// The timeout is measured using the host's system clock.
171    RealTime,
172}
173
174/// Whether the timeout is relative or absolute.
175#[derive(Debug, Copy, Clone)]
176pub enum TimeoutStyle {
177    /// The given duration is interpreted relative to "now" for the selected clock.
178    Relative,
179    /// The given duration is interpreted as an "absolute" time, i.e., relative to the epoch of the
180    /// selected clock.
181    Absolute,
182}
183
184impl MiriMachine<'_> {
185    /// Computes the deadline for a given timeout configuration and duration.
186    pub fn timeout(
187        &self,
188        clock: TimeoutClock,
189        style: TimeoutStyle,
190        duration: Duration,
191    ) -> Deadline {
192        // First let's figure out what "zero" means for the given clock and style.
193        let zero = match clock {
194            TimeoutClock::RealTime => {
195                assert!(self.communicate(), "cannot have `RealTime` timeout with isolation");
196                Deadline::RealTime(match style {
197                    TimeoutStyle::Absolute => SystemTime::UNIX_EPOCH,
198                    TimeoutStyle::Relative => SystemTime::now(),
199                })
200            }
201            TimeoutClock::Monotonic =>
202                Deadline::Monotonic(match style {
203                    TimeoutStyle::Absolute => self.monotonic_clock.epoch(),
204                    TimeoutStyle::Relative => self.monotonic_clock.now(),
205                }),
206        };
207        // Then add the given duration relative to that "zero".
208        zero.add_lossy(duration)
209    }
210}