Skip to content

Commit bc307b4

Browse files
RalfJungpvdrz
authored andcommitted
organize clock arithmetic more like the stdlib
1 parent f5e2f73 commit bc307b4

File tree

5 files changed

+50
-61
lines changed

5 files changed

+50
-61
lines changed

src/clock.rs

Lines changed: 38 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,32 @@ enum InstantKind {
2020
Virtual { nanoseconds: u64 },
2121
}
2222

23+
impl Instant {
24+
pub fn checked_add(&self, duration: Duration) -> Option<Instant> {
25+
match self.kind {
26+
InstantKind::Host(instant) =>
27+
instant.checked_add(duration).map(|i| Instant { kind: InstantKind::Host(i) }),
28+
InstantKind::Virtual { nanoseconds } =>
29+
u128::from(nanoseconds)
30+
.checked_add(duration.as_nanos())
31+
.and_then(|n| u64::try_from(n).ok())
32+
.map(|nanoseconds| Instant { kind: InstantKind::Virtual { nanoseconds } }),
33+
}
34+
}
35+
36+
pub fn duration_since(&self, earlier: Instant) -> Duration {
37+
match (&self.kind, earlier.kind) {
38+
(InstantKind::Host(instant), InstantKind::Host(earlier)) =>
39+
instant.duration_since(earlier),
40+
(
41+
InstantKind::Virtual { nanoseconds },
42+
InstantKind::Virtual { nanoseconds: earlier },
43+
) => Duration::from_nanos(nanoseconds.saturating_sub(earlier)),
44+
_ => panic!("all `Instant` must be of the same kind"),
45+
}
46+
}
47+
}
48+
2349
/// A monotone clock used for `Instant` simulation.
2450
#[derive(Debug)]
2551
pub struct Clock {
@@ -50,24 +76,14 @@ impl Clock {
5076
Self { kind }
5177
}
5278

53-
/// Get the current time relative to this clock.
54-
pub fn get(&self) -> Duration {
55-
match &self.kind {
56-
ClockKind::Host { time_anchor } =>
57-
StdInstant::now().saturating_duration_since(*time_anchor),
58-
ClockKind::Virtual { nanoseconds } =>
59-
Duration::from_nanos(nanoseconds.load(Ordering::Relaxed)),
60-
}
61-
}
62-
6379
/// Let the time pass for a small interval.
6480
pub fn tick(&self) {
6581
match &self.kind {
6682
ClockKind::Host { .. } => {
6783
// Time will pass without us doing anything.
6884
}
6985
ClockKind::Virtual { nanoseconds } => {
70-
nanoseconds.fetch_add(NANOSECOND_PER_BASIC_BLOCK, Ordering::Relaxed);
86+
nanoseconds.fetch_add(NANOSECOND_PER_BASIC_BLOCK, Ordering::SeqCst);
7187
}
7288
}
7389
}
@@ -78,54 +94,26 @@ impl Clock {
7894
ClockKind::Host { .. } => std::thread::sleep(duration),
7995
ClockKind::Virtual { nanoseconds } => {
8096
// Just pretend that we have slept for some time.
81-
nanoseconds.fetch_add(duration.as_nanos().try_into().unwrap(), Ordering::Relaxed);
97+
nanoseconds.fetch_add(duration.as_nanos().try_into().unwrap(), Ordering::SeqCst);
8298
}
8399
}
84100
}
85101

86-
/// Compute `now + duration` relative to this clock.
87-
pub fn get_time_relative(&self, duration: Duration) -> Option<Instant> {
102+
/// Return the `anchor` instant, to convert between monotone instants and durations relative to the anchor.
103+
pub fn anchor(&self) -> Instant {
88104
match &self.kind {
89-
ClockKind::Host { .. } =>
90-
StdInstant::now()
91-
.checked_add(duration)
92-
.map(|instant| Instant { kind: InstantKind::Host(instant) }),
93-
ClockKind::Virtual { nanoseconds } =>
94-
nanoseconds
95-
.load(Ordering::Relaxed)
96-
.checked_add(duration.as_nanos().try_into().unwrap())
97-
.map(|nanoseconds| Instant { kind: InstantKind::Virtual { nanoseconds } }),
105+
ClockKind::Host { time_anchor } => Instant { kind: InstantKind::Host(*time_anchor) },
106+
ClockKind::Virtual { .. } => Instant { kind: InstantKind::Virtual { nanoseconds: 0 } },
98107
}
99108
}
100109

101-
/// Compute `start + duration` relative to this clock where `start` is the instant of time when
102-
/// this clock was created.
103-
pub fn get_time_absolute(&self, duration: Duration) -> Option<Instant> {
110+
pub fn now(&self) -> Instant {
104111
match &self.kind {
105-
ClockKind::Host { time_anchor } =>
106-
time_anchor
107-
.checked_add(duration)
108-
.map(|instant| Instant { kind: InstantKind::Host(instant) }),
109-
ClockKind::Virtual { .. } =>
110-
Some(Instant {
111-
kind: InstantKind::Virtual {
112-
nanoseconds: duration.as_nanos().try_into().unwrap(),
113-
},
114-
}),
115-
}
116-
}
117-
118-
/// Returns the duration until the given instant.
119-
pub fn duration_until(&self, instant: &Instant) -> Duration {
120-
match (&instant.kind, &self.kind) {
121-
(InstantKind::Host(instant), ClockKind::Host { .. }) =>
122-
instant.saturating_duration_since(StdInstant::now()),
123-
(
124-
InstantKind::Virtual { nanoseconds },
125-
ClockKind::Virtual { nanoseconds: current_ns },
126-
) =>
127-
Duration::from_nanos(nanoseconds.saturating_sub(current_ns.load(Ordering::Relaxed))),
128-
_ => panic!(),
112+
ClockKind::Host { .. } => Instant { kind: InstantKind::Host(StdInstant::now()) },
113+
ClockKind::Virtual { nanoseconds } =>
114+
Instant {
115+
kind: InstantKind::Virtual { nanoseconds: nanoseconds.load(Ordering::SeqCst) },
116+
},
129117
}
130118
}
131119
}

src/concurrency/thread.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ impl Time {
191191
/// How long do we have to wait from now until the specified time?
192192
fn get_wait_time(&self, clock: &Clock) -> Duration {
193193
match self {
194-
Time::Monotonic(instant) => clock.duration_until(instant),
194+
Time::Monotonic(instant) => instant.duration_since(clock.now()),
195195
Time::RealTime(time) =>
196196
time.duration_since(SystemTime::now()).unwrap_or(Duration::new(0, 0)),
197197
}

src/shims/time.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
4141
this.check_no_isolation("`clock_gettime` with real time clocks")?;
4242
system_time_to_duration(&SystemTime::now())?
4343
} else if relative_clocks.contains(&clk_id) {
44-
this.machine.clock.get()
44+
this.machine.clock.now().duration_since(this.machine.clock.anchor())
4545
} else {
4646
let einval = this.eval_libc("EINVAL")?;
4747
this.set_last_error(einval)?;
@@ -125,7 +125,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
125125

126126
// QueryPerformanceCounter uses a hardware counter as its basis.
127127
// Miri will emulate a counter with a resolution of 1 nanosecond.
128-
let duration = this.machine.clock.get();
128+
let duration = this.machine.clock.now().duration_since(this.machine.clock.anchor());
129129
let qpc = i64::try_from(duration.as_nanos()).map_err(|_| {
130130
err_unsup_format!("programs running longer than 2^63 nanoseconds are not supported")
131131
})?;
@@ -164,7 +164,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
164164

165165
// This returns a u64, with time units determined dynamically by `mach_timebase_info`.
166166
// We return plain nanoseconds.
167-
let duration = this.machine.clock.get();
167+
let duration = this.machine.clock.now().duration_since(this.machine.clock.anchor());
168168
let res = u64::try_from(duration.as_nanos()).map_err(|_| {
169169
err_unsup_format!("programs running longer than 2^64 nanoseconds are not supported")
170170
})?;
@@ -207,9 +207,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
207207
}
208208
};
209209
// If adding the duration overflows, let's just sleep for an hour. Waking up early is always acceptable.
210-
let timeout_time = this.machine.clock.get_time_relative(duration).unwrap_or_else(|| {
211-
this.machine.clock.get_time_relative(Duration::from_secs(3600)).unwrap()
212-
});
210+
let now = this.machine.clock.now();
211+
let timeout_time = now
212+
.checked_add(duration)
213+
.unwrap_or_else(|| now.checked_add(Duration::from_secs(3600)).unwrap());
213214

214215
let active_thread = this.get_active_thread();
215216
this.block_thread(active_thread);
@@ -235,7 +236,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
235236
let timeout_ms = this.read_scalar(timeout)?.to_u32()?;
236237

237238
let duration = Duration::from_millis(timeout_ms.into());
238-
let timeout_time = this.machine.clock.get_time_relative(duration).unwrap();
239+
let timeout_time = this.machine.clock.now().checked_add(duration).unwrap();
239240

240241
let active_thread = this.get_active_thread();
241242
this.block_thread(active_thread);

src/shims/unix/linux/sync.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,14 +106,14 @@ pub fn futex<'tcx>(
106106
if op & futex_realtime != 0 {
107107
Time::RealTime(SystemTime::UNIX_EPOCH.checked_add(duration).unwrap())
108108
} else {
109-
Time::Monotonic(this.machine.clock.get_time_absolute(duration).unwrap())
109+
Time::Monotonic(this.machine.clock.anchor().checked_add(duration).unwrap())
110110
}
111111
} else {
112112
// FUTEX_WAIT uses a relative timestamp.
113113
if op & futex_realtime != 0 {
114114
Time::RealTime(SystemTime::now().checked_add(duration).unwrap())
115115
} else {
116-
Time::Monotonic(this.machine.clock.get_time_relative(duration).unwrap())
116+
Time::Monotonic(this.machine.clock.now().checked_add(duration).unwrap())
117117
}
118118
})
119119
};

src/shims/unix/sync.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -840,7 +840,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
840840
let timeout_time = if clock_id == this.eval_libc_i32("CLOCK_REALTIME")? {
841841
Time::RealTime(SystemTime::UNIX_EPOCH.checked_add(duration).unwrap())
842842
} else if clock_id == this.eval_libc_i32("CLOCK_MONOTONIC")? {
843-
Time::Monotonic(this.machine.clock.get_time_absolute(duration).unwrap())
843+
Time::Monotonic(this.machine.clock.anchor().checked_add(duration).unwrap())
844844
} else {
845845
throw_unsup_format!("unsupported clock id: {}", clock_id);
846846
};

0 commit comments

Comments
 (0)