organize clock arithmetic more like the stdlib
This commit is contained in:
parent
f5e2f73178
commit
bc307b40ba
94
src/clock.rs
94
src/clock.rs
@ -20,6 +20,32 @@ enum InstantKind {
|
||||
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 {
|
||||
@ -50,16 +76,6 @@ pub fn new(communicate: bool) -> Self {
|
||||
Self { kind }
|
||||
}
|
||||
|
||||
/// Get the current time relative to this clock.
|
||||
pub fn get(&self) -> Duration {
|
||||
match &self.kind {
|
||||
ClockKind::Host { time_anchor } =>
|
||||
StdInstant::now().saturating_duration_since(*time_anchor),
|
||||
ClockKind::Virtual { nanoseconds } =>
|
||||
Duration::from_nanos(nanoseconds.load(Ordering::Relaxed)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Let the time pass for a small interval.
|
||||
pub fn tick(&self) {
|
||||
match &self.kind {
|
||||
@ -67,7 +83,7 @@ pub fn tick(&self) {
|
||||
// Time will pass without us doing anything.
|
||||
}
|
||||
ClockKind::Virtual { nanoseconds } => {
|
||||
nanoseconds.fetch_add(NANOSECOND_PER_BASIC_BLOCK, Ordering::Relaxed);
|
||||
nanoseconds.fetch_add(NANOSECOND_PER_BASIC_BLOCK, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -78,54 +94,26 @@ pub fn sleep(&self, duration: Duration) {
|
||||
ClockKind::Host { .. } => std::thread::sleep(duration),
|
||||
ClockKind::Virtual { nanoseconds } => {
|
||||
// Just pretend that we have slept for some time.
|
||||
nanoseconds.fetch_add(duration.as_nanos().try_into().unwrap(), Ordering::Relaxed);
|
||||
nanoseconds.fetch_add(duration.as_nanos().try_into().unwrap(), Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute `now + duration` relative to this clock.
|
||||
pub fn get_time_relative(&self, duration: Duration) -> Option<Instant> {
|
||||
/// 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 { .. } =>
|
||||
StdInstant::now()
|
||||
.checked_add(duration)
|
||||
.map(|instant| Instant { kind: InstantKind::Host(instant) }),
|
||||
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 } =>
|
||||
nanoseconds
|
||||
.load(Ordering::Relaxed)
|
||||
.checked_add(duration.as_nanos().try_into().unwrap())
|
||||
.map(|nanoseconds| Instant { kind: InstantKind::Virtual { nanoseconds } }),
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute `start + duration` relative to this clock where `start` is the instant of time when
|
||||
/// this clock was created.
|
||||
pub fn get_time_absolute(&self, duration: Duration) -> Option<Instant> {
|
||||
match &self.kind {
|
||||
ClockKind::Host { time_anchor } =>
|
||||
time_anchor
|
||||
.checked_add(duration)
|
||||
.map(|instant| Instant { kind: InstantKind::Host(instant) }),
|
||||
ClockKind::Virtual { .. } =>
|
||||
Some(Instant {
|
||||
kind: InstantKind::Virtual {
|
||||
nanoseconds: duration.as_nanos().try_into().unwrap(),
|
||||
},
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the duration until the given instant.
|
||||
pub fn duration_until(&self, instant: &Instant) -> Duration {
|
||||
match (&instant.kind, &self.kind) {
|
||||
(InstantKind::Host(instant), ClockKind::Host { .. }) =>
|
||||
instant.saturating_duration_since(StdInstant::now()),
|
||||
(
|
||||
InstantKind::Virtual { nanoseconds },
|
||||
ClockKind::Virtual { nanoseconds: current_ns },
|
||||
) =>
|
||||
Duration::from_nanos(nanoseconds.saturating_sub(current_ns.load(Ordering::Relaxed))),
|
||||
_ => panic!(),
|
||||
Instant {
|
||||
kind: InstantKind::Virtual { nanoseconds: nanoseconds.load(Ordering::SeqCst) },
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -191,7 +191,7 @@ impl Time {
|
||||
/// How long do we have to wait from now until the specified time?
|
||||
fn get_wait_time(&self, clock: &Clock) -> Duration {
|
||||
match self {
|
||||
Time::Monotonic(instant) => clock.duration_until(instant),
|
||||
Time::Monotonic(instant) => instant.duration_since(clock.now()),
|
||||
Time::RealTime(time) =>
|
||||
time.duration_since(SystemTime::now()).unwrap_or(Duration::new(0, 0)),
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ fn clock_gettime(
|
||||
this.check_no_isolation("`clock_gettime` with real time clocks")?;
|
||||
system_time_to_duration(&SystemTime::now())?
|
||||
} else if relative_clocks.contains(&clk_id) {
|
||||
this.machine.clock.get()
|
||||
this.machine.clock.now().duration_since(this.machine.clock.anchor())
|
||||
} else {
|
||||
let einval = this.eval_libc("EINVAL")?;
|
||||
this.set_last_error(einval)?;
|
||||
@ -125,7 +125,7 @@ fn QueryPerformanceCounter(
|
||||
|
||||
// QueryPerformanceCounter uses a hardware counter as its basis.
|
||||
// Miri will emulate a counter with a resolution of 1 nanosecond.
|
||||
let duration = this.machine.clock.get();
|
||||
let duration = this.machine.clock.now().duration_since(this.machine.clock.anchor());
|
||||
let qpc = i64::try_from(duration.as_nanos()).map_err(|_| {
|
||||
err_unsup_format!("programs running longer than 2^63 nanoseconds are not supported")
|
||||
})?;
|
||||
@ -164,7 +164,7 @@ fn mach_absolute_time(&self) -> InterpResult<'tcx, Scalar<Provenance>> {
|
||||
|
||||
// This returns a u64, with time units determined dynamically by `mach_timebase_info`.
|
||||
// We return plain nanoseconds.
|
||||
let duration = this.machine.clock.get();
|
||||
let duration = this.machine.clock.now().duration_since(this.machine.clock.anchor());
|
||||
let res = u64::try_from(duration.as_nanos()).map_err(|_| {
|
||||
err_unsup_format!("programs running longer than 2^64 nanoseconds are not supported")
|
||||
})?;
|
||||
@ -207,9 +207,10 @@ fn nanosleep(
|
||||
}
|
||||
};
|
||||
// If adding the duration overflows, let's just sleep for an hour. Waking up early is always acceptable.
|
||||
let timeout_time = this.machine.clock.get_time_relative(duration).unwrap_or_else(|| {
|
||||
this.machine.clock.get_time_relative(Duration::from_secs(3600)).unwrap()
|
||||
});
|
||||
let now = this.machine.clock.now();
|
||||
let timeout_time = now
|
||||
.checked_add(duration)
|
||||
.unwrap_or_else(|| now.checked_add(Duration::from_secs(3600)).unwrap());
|
||||
|
||||
let active_thread = this.get_active_thread();
|
||||
this.block_thread(active_thread);
|
||||
@ -235,7 +236,7 @@ fn Sleep(&mut self, timeout: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx> {
|
||||
let timeout_ms = this.read_scalar(timeout)?.to_u32()?;
|
||||
|
||||
let duration = Duration::from_millis(timeout_ms.into());
|
||||
let timeout_time = this.machine.clock.get_time_relative(duration).unwrap();
|
||||
let timeout_time = this.machine.clock.now().checked_add(duration).unwrap();
|
||||
|
||||
let active_thread = this.get_active_thread();
|
||||
this.block_thread(active_thread);
|
||||
|
@ -106,14 +106,14 @@ pub fn futex<'tcx>(
|
||||
if op & futex_realtime != 0 {
|
||||
Time::RealTime(SystemTime::UNIX_EPOCH.checked_add(duration).unwrap())
|
||||
} else {
|
||||
Time::Monotonic(this.machine.clock.get_time_absolute(duration).unwrap())
|
||||
Time::Monotonic(this.machine.clock.anchor().checked_add(duration).unwrap())
|
||||
}
|
||||
} else {
|
||||
// FUTEX_WAIT uses a relative timestamp.
|
||||
if op & futex_realtime != 0 {
|
||||
Time::RealTime(SystemTime::now().checked_add(duration).unwrap())
|
||||
} else {
|
||||
Time::Monotonic(this.machine.clock.get_time_relative(duration).unwrap())
|
||||
Time::Monotonic(this.machine.clock.now().checked_add(duration).unwrap())
|
||||
}
|
||||
})
|
||||
};
|
||||
|
@ -840,7 +840,7 @@ fn pthread_cond_timedwait(
|
||||
let timeout_time = if clock_id == this.eval_libc_i32("CLOCK_REALTIME")? {
|
||||
Time::RealTime(SystemTime::UNIX_EPOCH.checked_add(duration).unwrap())
|
||||
} else if clock_id == this.eval_libc_i32("CLOCK_MONOTONIC")? {
|
||||
Time::Monotonic(this.machine.clock.get_time_absolute(duration).unwrap())
|
||||
Time::Monotonic(this.machine.clock.anchor().checked_add(duration).unwrap())
|
||||
} else {
|
||||
throw_unsup_format!("unsupported clock id: {}", clock_id);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user