about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2022-09-01 12:18:55 +0200
committerChristian Poveda <git@pvdrz.com>2022-09-13 15:16:41 -0500
commitbc307b40bad4fe33319db3e61e779aacb853d4ca (patch)
treeac90f5770c0e2740c6824055f4d4f5d3e87e09e0
parentf5e2f731785029c2fa50f590f41ff14f5e83299d (diff)
downloadrust-bc307b40bad4fe33319db3e61e779aacb853d4ca.tar.gz
rust-bc307b40bad4fe33319db3e61e779aacb853d4ca.zip
organize clock arithmetic more like the stdlib
-rw-r--r--src/clock.rs88
-rw-r--r--src/concurrency/thread.rs2
-rw-r--r--src/shims/time.rs15
-rw-r--r--src/shims/unix/linux/sync.rs4
-rw-r--r--src/shims/unix/sync.rs2
5 files changed, 50 insertions, 61 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<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 @@ impl Clock {
         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 @@ impl Clock {
                 // 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 @@ impl Clock {
             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::Virtual { nanoseconds } =>
-                nanoseconds
-                    .load(Ordering::Relaxed)
-                    .checked_add(duration.as_nanos().try_into().unwrap())
-                    .map(|nanoseconds| Instant { kind: InstantKind::Virtual { nanoseconds } }),
+            ClockKind::Host { time_anchor } => Instant { kind: InstantKind::Host(*time_anchor) },
+            ClockKind::Virtual { .. } => Instant { kind: InstantKind::Virtual { nanoseconds: 0 } },
         }
     }
 
-    /// 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> {
+    pub fn now(&self) -> 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!(),
+            ClockKind::Host { .. } => Instant { kind: InstantKind::Host(StdInstant::now()) },
+            ClockKind::Virtual { nanoseconds } =>
+                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 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
             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 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, '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.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 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
 
         // 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 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
             }
         };
         // 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 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, '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 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
         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);
         };