Merge pull request #4242 from RalfJung/clock-names

machine clock: make 'monotonic' explicit
This commit is contained in:
Ralf Jung 2025-03-28 14:24:15 +00:00 committed by GitHub
commit 99ce8fc8db
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 34 additions and 30 deletions

View file

@ -62,12 +62,12 @@ impl Instant {
/// A monotone clock used for `Instant` simulation.
#[derive(Debug)]
pub struct Clock {
kind: ClockKind,
pub struct MonotonicClock {
kind: MonotonicClockKind,
}
#[derive(Debug)]
enum ClockKind {
enum MonotonicClockKind {
Host {
/// The "epoch" for this machine's monotone clock:
/// the moment we consider to be time = 0.
@ -79,13 +79,13 @@ enum ClockKind {
},
}
impl Clock {
impl MonotonicClock {
/// 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() }
MonotonicClockKind::Host { epoch: StdInstant::now() }
} else {
ClockKind::Virtual { nanoseconds: 0.into() }
MonotonicClockKind::Virtual { nanoseconds: 0.into() }
};
Self { kind }
@ -94,10 +94,10 @@ impl Clock {
/// Let the time pass for a small interval.
pub fn tick(&self) {
match &self.kind {
ClockKind::Host { .. } => {
MonotonicClockKind::Host { .. } => {
// Time will pass without us doing anything.
}
ClockKind::Virtual { nanoseconds } => {
MonotonicClockKind::Virtual { nanoseconds } => {
nanoseconds.update(|x| x + NANOSECONDS_PER_BASIC_BLOCK);
}
}
@ -106,8 +106,8 @@ impl Clock {
/// Sleep for the desired duration.
pub fn sleep(&self, duration: Duration) {
match &self.kind {
ClockKind::Host { .. } => std::thread::sleep(duration),
ClockKind::Virtual { nanoseconds } => {
MonotonicClockKind::Host { .. } => std::thread::sleep(duration),
MonotonicClockKind::Virtual { nanoseconds } => {
// Just pretend that we have slept for some time.
let nanos: u128 = duration.as_nanos();
nanoseconds.update(|x| {
@ -121,15 +121,17 @@ impl Clock {
/// 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 } },
MonotonicClockKind::Host { epoch } => Instant { kind: InstantKind::Host(*epoch) },
MonotonicClockKind::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 } =>
MonotonicClockKind::Host { .. } =>
Instant { kind: InstantKind::Host(StdInstant::now()) },
MonotonicClockKind::Virtual { nanoseconds } =>
Instant { kind: InstantKind::Virtual { nanoseconds: nanoseconds.get() } },
}
}

View file

@ -347,7 +347,7 @@ enum Timeout {
impl Timeout {
/// 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: &MonotonicClock) -> Duration {
match self {
Timeout::Monotonic(instant) => instant.duration_since(clock.now()),
Timeout::RealTime(time) =>
@ -683,7 +683,7 @@ impl<'tcx> ThreadManager<'tcx> {
}
/// Get the wait time for the next timeout, or `None` if no timeout is pending.
fn next_callback_wait_time(&self, clock: &Clock) -> Option<Duration> {
fn next_callback_wait_time(&self, clock: &MonotonicClock) -> Option<Duration> {
self.threads
.iter()
.filter_map(|t| {
@ -702,7 +702,7 @@ impl<'tcx> ThreadManager<'tcx> {
/// used in stateless model checkers such as Loom: run the active thread as
/// long as we can and switch only when we have to (the active thread was
/// blocked, terminated, or has explicitly asked to be preempted).
fn schedule(&mut self, clock: &Clock) -> InterpResult<'tcx, SchedulingAction> {
fn schedule(&mut self, clock: &MonotonicClock) -> InterpResult<'tcx, SchedulingAction> {
// This thread and the program can keep going.
if self.threads[self.active_thread].state.is_enabled() && !self.yield_active_thread {
// The currently active thread is still enabled, just continue with it.
@ -772,7 +772,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
for (id, thread) in this.machine.threads.threads.iter_enumerated_mut() {
match &thread.state {
ThreadState::Blocked { timeout: Some(timeout), .. }
if timeout.get_wait_time(&this.machine.clock) == Duration::ZERO =>
if timeout.get_wait_time(&this.machine.monotonic_clock) == Duration::ZERO =>
{
let old_state = mem::replace(&mut thread.state, ThreadState::Enabled);
let ThreadState::Blocked { callback, .. } = old_state else { unreachable!() };
@ -1006,8 +1006,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
TimeoutClock::Monotonic =>
Timeout::Monotonic(match anchor {
TimeoutAnchor::Absolute => this.machine.clock.epoch(),
TimeoutAnchor::Relative => this.machine.clock.now(),
TimeoutAnchor::Absolute => this.machine.monotonic_clock.epoch(),
TimeoutAnchor::Relative => this.machine.monotonic_clock.now(),
}),
};
anchor.add_lossy(duration)
@ -1152,7 +1152,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.machine.handle_abnormal_termination();
throw_machine_stop!(TerminationInfo::Interrupted);
}
match this.machine.threads.schedule(&this.machine.clock)? {
match this.machine.threads.schedule(&this.machine.monotonic_clock)? {
SchedulingAction::ExecuteStep => {
if !this.step()? {
// See if this thread can do something else.
@ -1167,7 +1167,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.run_timeout_callback()?;
}
SchedulingAction::Sleep(duration) => {
this.machine.clock.sleep(duration);
this.machine.monotonic_clock.sleep(duration);
}
}
}

View file

@ -121,7 +121,7 @@ pub use crate::borrow_tracker::stacked_borrows::{
};
pub use crate::borrow_tracker::tree_borrows::{EvalContextExt as _, Tree};
pub use crate::borrow_tracker::{BorTag, BorrowTrackerMethod, EvalContextExt as _, RetagFields};
pub use crate::clock::{Clock, Instant};
pub use crate::clock::{Instant, MonotonicClock};
pub use crate::concurrency::cpu_affinity::MAX_CPUS;
pub use crate::concurrency::data_race::{
AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, EvalContextExt as _,

View file

@ -486,7 +486,7 @@ pub struct MiriMachine<'tcx> {
pub(crate) epoll_interests: shims::EpollInterestTable,
/// This machine's monotone clock.
pub(crate) clock: Clock,
pub(crate) monotonic_clock: MonotonicClock,
/// The set of threads.
pub(crate) threads: ThreadManager<'tcx>,
@ -713,7 +713,7 @@ impl<'tcx> MiriMachine<'tcx> {
preemption_rate: config.preemption_rate,
report_progress: config.report_progress,
basic_block_count: 0,
clock: Clock::new(config.isolated_op == IsolatedOp::Allow),
monotonic_clock: MonotonicClock::new(config.isolated_op == IsolatedOp::Allow),
#[cfg(unix)]
native_lib: config.native_lib.as_ref().map(|lib_file_path| {
let host_triple = rustc_session::config::host_tuple();
@ -896,7 +896,7 @@ impl VisitProvenance for MiriMachine<'_> {
tcx: _,
isolated_op: _,
validation: _,
clock: _,
monotonic_clock: _,
layouts: _,
static_roots: _,
profiler: _,
@ -1568,7 +1568,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
ecx.maybe_preempt_active_thread();
// Make sure some time passes.
ecx.machine.clock.tick();
ecx.machine.monotonic_clock.tick();
interp_ok(())
}

View file

@ -79,7 +79,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.check_no_isolation("`clock_gettime` with `REALTIME` clocks")?;
system_time_to_duration(&SystemTime::now())?
} else if relative_clocks.contains(&clk_id) {
this.machine.clock.now().duration_since(this.machine.clock.epoch())
this.machine.monotonic_clock.now().duration_since(this.machine.monotonic_clock.epoch())
} else {
return this.set_last_error_and_return_i32(LibcError("EINVAL"));
};
@ -248,7 +248,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// QueryPerformanceCounter uses a hardware counter as its basis.
// Miri will emulate a counter with a resolution of 1 nanosecond.
let duration = this.machine.clock.now().duration_since(this.machine.clock.epoch());
let duration =
this.machine.monotonic_clock.now().duration_since(this.machine.monotonic_clock.epoch());
let qpc = i64::try_from(duration.as_nanos()).map_err(|_| {
err_unsup_format!("programs running longer than 2^63 nanoseconds are not supported")
})?;
@ -287,7 +288,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// This returns a u64, with time units determined dynamically by `mach_timebase_info`.
// We return plain nanoseconds.
let duration = this.machine.clock.now().duration_since(this.machine.clock.epoch());
let duration =
this.machine.monotonic_clock.now().duration_since(this.machine.monotonic_clock.epoch());
let res = u64::try_from(duration.as_nanos()).map_err(|_| {
err_unsup_format!("programs running longer than 2^64 nanoseconds are not supported")
})?;