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 },
|
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.
|
/// A monotone clock used for `Instant` simulation.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Clock {
|
pub struct Clock {
|
||||||
@ -50,16 +76,6 @@ pub fn new(communicate: bool) -> Self {
|
|||||||
Self { kind }
|
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.
|
/// Let the time pass for a small interval.
|
||||||
pub fn tick(&self) {
|
pub fn tick(&self) {
|
||||||
match &self.kind {
|
match &self.kind {
|
||||||
@ -67,7 +83,7 @@ pub fn tick(&self) {
|
|||||||
// Time will pass without us doing anything.
|
// Time will pass without us doing anything.
|
||||||
}
|
}
|
||||||
ClockKind::Virtual { nanoseconds } => {
|
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::Host { .. } => std::thread::sleep(duration),
|
||||||
ClockKind::Virtual { nanoseconds } => {
|
ClockKind::Virtual { nanoseconds } => {
|
||||||
// Just pretend that we have slept for some time.
|
// 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.
|
/// Return the `anchor` instant, to convert between monotone instants and durations relative to the anchor.
|
||||||
pub fn get_time_relative(&self, duration: Duration) -> Option<Instant> {
|
pub fn anchor(&self) -> Instant {
|
||||||
match &self.kind {
|
match &self.kind {
|
||||||
ClockKind::Host { .. } =>
|
ClockKind::Host { time_anchor } => Instant { kind: InstantKind::Host(*time_anchor) },
|
||||||
StdInstant::now()
|
ClockKind::Virtual { .. } => Instant { kind: InstantKind::Virtual { nanoseconds: 0 } },
|
||||||
.checked_add(duration)
|
}
|
||||||
.map(|instant| Instant { kind: InstantKind::Host(instant) }),
|
}
|
||||||
|
|
||||||
|
pub fn now(&self) -> Instant {
|
||||||
|
match &self.kind {
|
||||||
|
ClockKind::Host { .. } => Instant { kind: InstantKind::Host(StdInstant::now()) },
|
||||||
ClockKind::Virtual { nanoseconds } =>
|
ClockKind::Virtual { nanoseconds } =>
|
||||||
nanoseconds
|
Instant {
|
||||||
.load(Ordering::Relaxed)
|
kind: InstantKind::Virtual { nanoseconds: nanoseconds.load(Ordering::SeqCst) },
|
||||||
.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!(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -191,7 +191,7 @@ impl Time {
|
|||||||
/// How long do we have to wait from now until the specified time?
|
/// How long do we have to wait from now until the specified time?
|
||||||
fn get_wait_time(&self, clock: &Clock) -> Duration {
|
fn get_wait_time(&self, clock: &Clock) -> Duration {
|
||||||
match self {
|
match self {
|
||||||
Time::Monotonic(instant) => clock.duration_until(instant),
|
Time::Monotonic(instant) => instant.duration_since(clock.now()),
|
||||||
Time::RealTime(time) =>
|
Time::RealTime(time) =>
|
||||||
time.duration_since(SystemTime::now()).unwrap_or(Duration::new(0, 0)),
|
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")?;
|
this.check_no_isolation("`clock_gettime` with real time clocks")?;
|
||||||
system_time_to_duration(&SystemTime::now())?
|
system_time_to_duration(&SystemTime::now())?
|
||||||
} else if relative_clocks.contains(&clk_id) {
|
} else if relative_clocks.contains(&clk_id) {
|
||||||
this.machine.clock.get()
|
this.machine.clock.now().duration_since(this.machine.clock.anchor())
|
||||||
} else {
|
} else {
|
||||||
let einval = this.eval_libc("EINVAL")?;
|
let einval = this.eval_libc("EINVAL")?;
|
||||||
this.set_last_error(einval)?;
|
this.set_last_error(einval)?;
|
||||||
@ -125,7 +125,7 @@ fn QueryPerformanceCounter(
|
|||||||
|
|
||||||
// QueryPerformanceCounter uses a hardware counter as its basis.
|
// QueryPerformanceCounter uses a hardware counter as its basis.
|
||||||
// Miri will emulate a counter with a resolution of 1 nanosecond.
|
// 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(|_| {
|
let qpc = i64::try_from(duration.as_nanos()).map_err(|_| {
|
||||||
err_unsup_format!("programs running longer than 2^63 nanoseconds are not supported")
|
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`.
|
// This returns a u64, with time units determined dynamically by `mach_timebase_info`.
|
||||||
// We return plain nanoseconds.
|
// 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(|_| {
|
let res = u64::try_from(duration.as_nanos()).map_err(|_| {
|
||||||
err_unsup_format!("programs running longer than 2^64 nanoseconds are not supported")
|
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.
|
// 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(|| {
|
let now = this.machine.clock.now();
|
||||||
this.machine.clock.get_time_relative(Duration::from_secs(3600)).unwrap()
|
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();
|
let active_thread = this.get_active_thread();
|
||||||
this.block_thread(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 timeout_ms = this.read_scalar(timeout)?.to_u32()?;
|
||||||
|
|
||||||
let duration = Duration::from_millis(timeout_ms.into());
|
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();
|
let active_thread = this.get_active_thread();
|
||||||
this.block_thread(active_thread);
|
this.block_thread(active_thread);
|
||||||
|
@ -106,14 +106,14 @@ pub fn futex<'tcx>(
|
|||||||
if op & futex_realtime != 0 {
|
if op & futex_realtime != 0 {
|
||||||
Time::RealTime(SystemTime::UNIX_EPOCH.checked_add(duration).unwrap())
|
Time::RealTime(SystemTime::UNIX_EPOCH.checked_add(duration).unwrap())
|
||||||
} else {
|
} else {
|
||||||
Time::Monotonic(this.machine.clock.get_time_absolute(duration).unwrap())
|
Time::Monotonic(this.machine.clock.anchor().checked_add(duration).unwrap())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// FUTEX_WAIT uses a relative timestamp.
|
// FUTEX_WAIT uses a relative timestamp.
|
||||||
if op & futex_realtime != 0 {
|
if op & futex_realtime != 0 {
|
||||||
Time::RealTime(SystemTime::now().checked_add(duration).unwrap())
|
Time::RealTime(SystemTime::now().checked_add(duration).unwrap())
|
||||||
} else {
|
} 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")? {
|
let timeout_time = if clock_id == this.eval_libc_i32("CLOCK_REALTIME")? {
|
||||||
Time::RealTime(SystemTime::UNIX_EPOCH.checked_add(duration).unwrap())
|
Time::RealTime(SystemTime::UNIX_EPOCH.checked_add(duration).unwrap())
|
||||||
} else if clock_id == this.eval_libc_i32("CLOCK_MONOTONIC")? {
|
} 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 {
|
} else {
|
||||||
throw_unsup_format!("unsupported clock id: {}", clock_id);
|
throw_unsup_format!("unsupported clock id: {}", clock_id);
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user