Auto merge of #1243 - RalfJung:instant, r=RalfJung
implement Instant::now For now, this is Linux-only. Unlike `SystemTime`, we cannot convert `Instant` to something absolute via an epoch. But that's okay, that clock is relative anyway, so we just make up our own time anchor when interpretation starts. Fixes https://github.com/rust-lang/miri/issues/1242
This commit is contained in:
commit
ee71b2e140
6 changed files with 63 additions and 34 deletions
|
|
@ -5,6 +5,7 @@ use std::borrow::Cow;
|
|||
use std::cell::RefCell;
|
||||
use std::num::NonZeroU64;
|
||||
use std::rc::Rc;
|
||||
use std::time::Instant;
|
||||
|
||||
use rand::rngs::StdRng;
|
||||
|
||||
|
|
@ -164,6 +165,9 @@ pub struct Evaluator<'tcx> {
|
|||
/// the call to `miri_start_panic` (the panic payload) when unwinding.
|
||||
/// This is pointer-sized, and matches the `Payload` type in `src/libpanic_unwind/miri.rs`.
|
||||
pub(crate) panic_payload: Option<Scalar<Tag>>,
|
||||
|
||||
/// The "time anchor" for this machine's monotone clock (for `Instant` simulation).
|
||||
pub(crate) time_anchor: Instant,
|
||||
}
|
||||
|
||||
impl<'tcx> Evaluator<'tcx> {
|
||||
|
|
@ -182,6 +186,7 @@ impl<'tcx> Evaluator<'tcx> {
|
|||
file_handler: Default::default(),
|
||||
dir_handler: Default::default(),
|
||||
panic_payload: None,
|
||||
time_anchor: Instant::now(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,6 +66,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
let result = this.gettimeofday(args[0], args[1])?;
|
||||
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
|
||||
}
|
||||
"mach_absolute_time" => {
|
||||
let result = this.mach_absolute_time()?;
|
||||
this.write_scalar(Scalar::from_uint(result, dest.layout.size), dest)?;
|
||||
}
|
||||
|
||||
// Other shims
|
||||
"pthread_attr_get_np" => {
|
||||
|
|
|
|||
|
|
@ -574,8 +574,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
buf_op: OpTy<'tcx, Tag>,
|
||||
) -> InterpResult<'tcx, i32> {
|
||||
let this = self.eval_context_mut();
|
||||
this.check_no_isolation("stat")?;
|
||||
this.assert_platform("macos", "stat");
|
||||
this.check_no_isolation("stat")?;
|
||||
// `stat` always follows symlinks.
|
||||
this.macos_stat_or_lstat(true, path_op, buf_op)
|
||||
}
|
||||
|
|
@ -587,8 +587,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
buf_op: OpTy<'tcx, Tag>,
|
||||
) -> InterpResult<'tcx, i32> {
|
||||
let this = self.eval_context_mut();
|
||||
this.check_no_isolation("lstat")?;
|
||||
this.assert_platform("macos", "lstat");
|
||||
this.check_no_isolation("lstat")?;
|
||||
this.macos_stat_or_lstat(false, path_op, buf_op)
|
||||
}
|
||||
|
||||
|
|
@ -599,8 +599,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
) -> InterpResult<'tcx, i32> {
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
this.check_no_isolation("fstat")?;
|
||||
this.assert_platform("macos", "fstat");
|
||||
this.check_no_isolation("fstat")?;
|
||||
|
||||
let fd = this.read_scalar(fd_op)?.to_i32()?;
|
||||
|
||||
|
|
@ -621,8 +621,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
) -> InterpResult<'tcx, i32> {
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
this.check_no_isolation("statx")?;
|
||||
this.assert_platform("linux", "statx");
|
||||
this.check_no_isolation("statx")?;
|
||||
|
||||
let statxbuf_scalar = this.read_scalar(statxbuf_op)?.not_undef()?;
|
||||
let pathname_scalar = this.read_scalar(pathname_op)?.not_undef()?;
|
||||
|
|
@ -880,8 +880,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
) -> InterpResult<'tcx, i32> {
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
this.check_no_isolation("readdir64_r")?;
|
||||
this.assert_platform("linux", "readdir64_r");
|
||||
this.check_no_isolation("readdir64_r")?;
|
||||
|
||||
let dirp = this.read_scalar(dirp_op)?.to_machine_usize(this)?;
|
||||
|
||||
|
|
@ -967,8 +967,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
) -> InterpResult<'tcx, i32> {
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
this.check_no_isolation("readdir_r")?;
|
||||
this.assert_platform("macos", "readdir_r");
|
||||
this.check_no_isolation("readdir_r")?;
|
||||
|
||||
let dirp = this.read_scalar(dirp_op)?.to_machine_usize(this)?;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,10 @@
|
|||
use std::time::{Duration, SystemTime};
|
||||
use std::time::{Duration, SystemTime, Instant};
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use crate::stacked_borrows::Tag;
|
||||
use crate::*;
|
||||
use helpers::immty_from_int_checked;
|
||||
|
||||
// Returns the time elapsed between now and the unix epoch as a `Duration`.
|
||||
fn get_time<'tcx>() -> InterpResult<'tcx, Duration> {
|
||||
system_time_to_duration(&SystemTime::now())
|
||||
}
|
||||
|
||||
/// Returns the time elapsed between the provided time and the unix epoch as a `Duration`.
|
||||
pub fn system_time_to_duration<'tcx>(time: &SystemTime) -> InterpResult<'tcx, Duration> {
|
||||
time.duration_since(SystemTime::UNIX_EPOCH)
|
||||
|
|
@ -17,7 +13,6 @@ pub fn system_time_to_duration<'tcx>(time: &SystemTime) -> InterpResult<'tcx, Du
|
|||
|
||||
impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
|
||||
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
|
||||
// Foreign function used by linux
|
||||
fn clock_gettime(
|
||||
&mut self,
|
||||
clk_id_op: OpTy<'tcx, Tag>,
|
||||
|
|
@ -25,18 +20,24 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
) -> InterpResult<'tcx, i32> {
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
this.assert_platform("linux", "clock_gettime");
|
||||
this.check_no_isolation("clock_gettime")?;
|
||||
|
||||
let clk_id = this.read_scalar(clk_id_op)?.to_i32()?;
|
||||
if clk_id != this.eval_libc_i32("CLOCK_REALTIME")? {
|
||||
let tp = this.deref_operand(tp_op)?;
|
||||
|
||||
let duration = if clk_id == this.eval_libc_i32("CLOCK_REALTIME")? {
|
||||
system_time_to_duration(&SystemTime::now())?
|
||||
} else if clk_id == this.eval_libc_i32("CLOCK_MONOTONIC")? {
|
||||
// Absolute time does not matter, only relative time does, so we can just
|
||||
// use our own time anchor here.
|
||||
Instant::now().duration_since(this.machine.time_anchor)
|
||||
} else {
|
||||
let einval = this.eval_libc("EINVAL")?;
|
||||
this.set_last_error(einval)?;
|
||||
return Ok(-1);
|
||||
}
|
||||
};
|
||||
|
||||
let tp = this.deref_operand(tp_op)?;
|
||||
|
||||
let duration = get_time()?;
|
||||
let tv_sec = duration.as_secs();
|
||||
let tv_nsec = duration.subsec_nanos();
|
||||
|
||||
|
|
@ -49,7 +50,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
|
||||
Ok(0)
|
||||
}
|
||||
// Foreign function used by generic unix (in particular macOS)
|
||||
|
||||
fn gettimeofday(
|
||||
&mut self,
|
||||
tv_op: OpTy<'tcx, Tag>,
|
||||
|
|
@ -57,7 +58,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
) -> InterpResult<'tcx, i32> {
|
||||
let this = self.eval_context_mut();
|
||||
|
||||
this.assert_platform("macos", "gettimeofday");
|
||||
this.check_no_isolation("gettimeofday")?;
|
||||
|
||||
// Using tz is obsolete and should always be null
|
||||
let tz = this.read_scalar(tz_op)?.not_undef()?;
|
||||
if !this.is_null(tz)? {
|
||||
|
|
@ -68,7 +71,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
|
||||
let tv = this.deref_operand(tv_op)?;
|
||||
|
||||
let duration = get_time()?;
|
||||
let duration = system_time_to_duration(&SystemTime::now())?;
|
||||
let tv_sec = duration.as_secs();
|
||||
let tv_usec = duration.subsec_micros();
|
||||
|
||||
|
|
@ -81,4 +84,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn mach_absolute_time(&self) -> InterpResult<'tcx, u64> {
|
||||
let this = self.eval_context_ref();
|
||||
|
||||
this.assert_platform("macos", "mach_absolute_time");
|
||||
this.check_no_isolation("mach_absolute_time")?;
|
||||
|
||||
// This returns a u64, with time units determined dynamically by `mach_timebase_info`.
|
||||
// We return plain nanoseconds.
|
||||
let duration = Instant::now().duration_since(this.machine.time_anchor);
|
||||
u64::try_from(duration.as_nanos())
|
||||
.map_err(|_| err_unsup_format!("programs running longer than 2^64 nanoseconds are not supported").into())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +0,0 @@
|
|||
// ignore-windows: TODO clock shims are not implemented on Windows
|
||||
// compile-flags: -Zmiri-disable-isolation
|
||||
|
||||
use std::time::SystemTime;
|
||||
|
||||
fn main() {
|
||||
let now1 = SystemTime::now();
|
||||
|
||||
// Do some work to make time pass.
|
||||
for _ in 0..10 { drop(vec![42]); }
|
||||
|
||||
let now2 = SystemTime::now();
|
||||
assert!(now2 > now1);
|
||||
}
|
||||
18
tests/run-pass/time.rs
Normal file
18
tests/run-pass/time.rs
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
// ignore-windows: TODO clock shims are not implemented on Windows
|
||||
// compile-flags: -Zmiri-disable-isolation
|
||||
|
||||
use std::time::{SystemTime, Instant};
|
||||
|
||||
fn main() {
|
||||
let now1 = SystemTime::now();
|
||||
// Do some work to make time pass.
|
||||
for _ in 0..10 { drop(vec![42]); }
|
||||
let now2 = SystemTime::now();
|
||||
assert!(now2 > now1);
|
||||
|
||||
let now1 = Instant::now();
|
||||
// Do some work to make time pass.
|
||||
for _ in 0..10 { drop(vec![42]); }
|
||||
let now2 = Instant::now();
|
||||
assert!(now2 > now1);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue