From bc307b40bad4fe33319db3e61e779aacb853d4ca Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 1 Sep 2022 12:18:55 +0200 Subject: [PATCH] organize clock arithmetic more like the stdlib --- src/clock.rs | 94 ++++++++++++++++-------------------- src/concurrency/thread.rs | 2 +- src/shims/time.rs | 15 +++--- src/shims/unix/linux/sync.rs | 4 +- src/shims/unix/sync.rs | 2 +- 5 files changed, 53 insertions(+), 64 deletions(-) diff --git a/src/clock.rs b/src/clock.rs index faf2d7fda6c..4fab2b2c5f3 100644 --- a/src/clock.rs +++ b/src/clock.rs @@ -20,6 +20,32 @@ enum InstantKind { Virtual { nanoseconds: u64 }, } +impl Instant { + pub fn checked_add(&self, duration: Duration) -> Option { + 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 { + /// 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 { - 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) }, + }, } } } diff --git a/src/concurrency/thread.rs b/src/concurrency/thread.rs index b39a6716848..5364b341ae3 100644 --- a/src/concurrency/thread.rs +++ b/src/concurrency/thread.rs @@ -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)), } diff --git a/src/shims/time.rs b/src/shims/time.rs index 46164e478f8..933c298ee4d 100644 --- a/src/shims/time.rs +++ b/src/shims/time.rs @@ -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> { // 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); diff --git a/src/shims/unix/linux/sync.rs b/src/shims/unix/linux/sync.rs index b2c2fa14d52..cf5a945c5fa 100644 --- a/src/shims/unix/linux/sync.rs +++ b/src/shims/unix/linux/sync.rs @@ -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()) } }) }; diff --git a/src/shims/unix/sync.rs b/src/shims/unix/sync.rs index 446293e03cb..496985fd083 100644 --- a/src/shims/unix/sync.rs +++ b/src/shims/unix/sync.rs @@ -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); };