Skip to content

Commit ee71b2e

Browse files
committed
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 #1242
2 parents 0cfb20d + f430e54 commit ee71b2e

File tree

6 files changed

+63
-34
lines changed

6 files changed

+63
-34
lines changed

src/machine.rs

+5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use std::borrow::Cow;
55
use std::cell::RefCell;
66
use std::num::NonZeroU64;
77
use std::rc::Rc;
8+
use std::time::Instant;
89

910
use rand::rngs::StdRng;
1011

@@ -164,6 +165,9 @@ pub struct Evaluator<'tcx> {
164165
/// the call to `miri_start_panic` (the panic payload) when unwinding.
165166
/// This is pointer-sized, and matches the `Payload` type in `src/libpanic_unwind/miri.rs`.
166167
pub(crate) panic_payload: Option<Scalar<Tag>>,
168+
169+
/// The "time anchor" for this machine's monotone clock (for `Instant` simulation).
170+
pub(crate) time_anchor: Instant,
167171
}
168172

169173
impl<'tcx> Evaluator<'tcx> {
@@ -182,6 +186,7 @@ impl<'tcx> Evaluator<'tcx> {
182186
file_handler: Default::default(),
183187
dir_handler: Default::default(),
184188
panic_payload: None,
189+
time_anchor: Instant::now(),
185190
}
186191
}
187192
}

src/shims/foreign_items/posix/macos.rs

+4
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
6666
let result = this.gettimeofday(args[0], args[1])?;
6767
this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?;
6868
}
69+
"mach_absolute_time" => {
70+
let result = this.mach_absolute_time()?;
71+
this.write_scalar(Scalar::from_uint(result, dest.layout.size), dest)?;
72+
}
6973

7074
// Other shims
7175
"pthread_attr_get_np" => {

src/shims/fs.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -574,8 +574,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
574574
buf_op: OpTy<'tcx, Tag>,
575575
) -> InterpResult<'tcx, i32> {
576576
let this = self.eval_context_mut();
577-
this.check_no_isolation("stat")?;
578577
this.assert_platform("macos", "stat");
578+
this.check_no_isolation("stat")?;
579579
// `stat` always follows symlinks.
580580
this.macos_stat_or_lstat(true, path_op, buf_op)
581581
}
@@ -587,8 +587,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
587587
buf_op: OpTy<'tcx, Tag>,
588588
) -> InterpResult<'tcx, i32> {
589589
let this = self.eval_context_mut();
590-
this.check_no_isolation("lstat")?;
591590
this.assert_platform("macos", "lstat");
591+
this.check_no_isolation("lstat")?;
592592
this.macos_stat_or_lstat(false, path_op, buf_op)
593593
}
594594

@@ -599,8 +599,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
599599
) -> InterpResult<'tcx, i32> {
600600
let this = self.eval_context_mut();
601601

602-
this.check_no_isolation("fstat")?;
603602
this.assert_platform("macos", "fstat");
603+
this.check_no_isolation("fstat")?;
604604

605605
let fd = this.read_scalar(fd_op)?.to_i32()?;
606606

@@ -621,8 +621,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
621621
) -> InterpResult<'tcx, i32> {
622622
let this = self.eval_context_mut();
623623

624-
this.check_no_isolation("statx")?;
625624
this.assert_platform("linux", "statx");
625+
this.check_no_isolation("statx")?;
626626

627627
let statxbuf_scalar = this.read_scalar(statxbuf_op)?.not_undef()?;
628628
let pathname_scalar = this.read_scalar(pathname_op)?.not_undef()?;
@@ -880,8 +880,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
880880
) -> InterpResult<'tcx, i32> {
881881
let this = self.eval_context_mut();
882882

883-
this.check_no_isolation("readdir64_r")?;
884883
this.assert_platform("linux", "readdir64_r");
884+
this.check_no_isolation("readdir64_r")?;
885885

886886
let dirp = this.read_scalar(dirp_op)?.to_machine_usize(this)?;
887887

@@ -967,8 +967,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
967967
) -> InterpResult<'tcx, i32> {
968968
let this = self.eval_context_mut();
969969

970-
this.check_no_isolation("readdir_r")?;
971970
this.assert_platform("macos", "readdir_r");
971+
this.check_no_isolation("readdir_r")?;
972972

973973
let dirp = this.read_scalar(dirp_op)?.to_machine_usize(this)?;
974974

src/shims/time.rs

+30-14
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
1-
use std::time::{Duration, SystemTime};
1+
use std::time::{Duration, SystemTime, Instant};
2+
use std::convert::TryFrom;
23

34
use crate::stacked_borrows::Tag;
45
use crate::*;
56
use helpers::immty_from_int_checked;
67

7-
// Returns the time elapsed between now and the unix epoch as a `Duration`.
8-
fn get_time<'tcx>() -> InterpResult<'tcx, Duration> {
9-
system_time_to_duration(&SystemTime::now())
10-
}
11-
128
/// Returns the time elapsed between the provided time and the unix epoch as a `Duration`.
139
pub fn system_time_to_duration<'tcx>(time: &SystemTime) -> InterpResult<'tcx, Duration> {
1410
time.duration_since(SystemTime::UNIX_EPOCH)
@@ -17,26 +13,31 @@ pub fn system_time_to_duration<'tcx>(time: &SystemTime) -> InterpResult<'tcx, Du
1713

1814
impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
1915
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
20-
// Foreign function used by linux
2116
fn clock_gettime(
2217
&mut self,
2318
clk_id_op: OpTy<'tcx, Tag>,
2419
tp_op: OpTy<'tcx, Tag>,
2520
) -> InterpResult<'tcx, i32> {
2621
let this = self.eval_context_mut();
2722

23+
this.assert_platform("linux", "clock_gettime");
2824
this.check_no_isolation("clock_gettime")?;
2925

3026
let clk_id = this.read_scalar(clk_id_op)?.to_i32()?;
31-
if clk_id != this.eval_libc_i32("CLOCK_REALTIME")? {
27+
let tp = this.deref_operand(tp_op)?;
28+
29+
let duration = if clk_id == this.eval_libc_i32("CLOCK_REALTIME")? {
30+
system_time_to_duration(&SystemTime::now())?
31+
} else if clk_id == this.eval_libc_i32("CLOCK_MONOTONIC")? {
32+
// Absolute time does not matter, only relative time does, so we can just
33+
// use our own time anchor here.
34+
Instant::now().duration_since(this.machine.time_anchor)
35+
} else {
3236
let einval = this.eval_libc("EINVAL")?;
3337
this.set_last_error(einval)?;
3438
return Ok(-1);
35-
}
36-
37-
let tp = this.deref_operand(tp_op)?;
39+
};
3840

39-
let duration = get_time()?;
4041
let tv_sec = duration.as_secs();
4142
let tv_nsec = duration.subsec_nanos();
4243

@@ -49,15 +50,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
4950

5051
Ok(0)
5152
}
52-
// Foreign function used by generic unix (in particular macOS)
53+
5354
fn gettimeofday(
5455
&mut self,
5556
tv_op: OpTy<'tcx, Tag>,
5657
tz_op: OpTy<'tcx, Tag>,
5758
) -> InterpResult<'tcx, i32> {
5859
let this = self.eval_context_mut();
5960

61+
this.assert_platform("macos", "gettimeofday");
6062
this.check_no_isolation("gettimeofday")?;
63+
6164
// Using tz is obsolete and should always be null
6265
let tz = this.read_scalar(tz_op)?.not_undef()?;
6366
if !this.is_null(tz)? {
@@ -68,7 +71,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
6871

6972
let tv = this.deref_operand(tv_op)?;
7073

71-
let duration = get_time()?;
74+
let duration = system_time_to_duration(&SystemTime::now())?;
7275
let tv_sec = duration.as_secs();
7376
let tv_usec = duration.subsec_micros();
7477

@@ -81,4 +84,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
8184

8285
Ok(0)
8386
}
87+
88+
fn mach_absolute_time(&self) -> InterpResult<'tcx, u64> {
89+
let this = self.eval_context_ref();
90+
91+
this.assert_platform("macos", "mach_absolute_time");
92+
this.check_no_isolation("mach_absolute_time")?;
93+
94+
// This returns a u64, with time units determined dynamically by `mach_timebase_info`.
95+
// We return plain nanoseconds.
96+
let duration = Instant::now().duration_since(this.machine.time_anchor);
97+
u64::try_from(duration.as_nanos())
98+
.map_err(|_| err_unsup_format!("programs running longer than 2^64 nanoseconds are not supported").into())
99+
}
84100
}

tests/run-pass/clock.rs

-14
This file was deleted.

tests/run-pass/time.rs

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// ignore-windows: TODO clock shims are not implemented on Windows
2+
// compile-flags: -Zmiri-disable-isolation
3+
4+
use std::time::{SystemTime, Instant};
5+
6+
fn main() {
7+
let now1 = SystemTime::now();
8+
// Do some work to make time pass.
9+
for _ in 0..10 { drop(vec![42]); }
10+
let now2 = SystemTime::now();
11+
assert!(now2 > now1);
12+
13+
let now1 = Instant::now();
14+
// Do some work to make time pass.
15+
for _ in 0..10 { drop(vec![42]); }
16+
let now2 = Instant::now();
17+
assert!(now2 > now1);
18+
}

0 commit comments

Comments
 (0)