Skip to content

Commit bd504d0

Browse files
committed
make sleep work with isolation enabled
1 parent 53138c6 commit bd504d0

File tree

6 files changed

+95
-26
lines changed

6 files changed

+95
-26
lines changed

src/concurrency/thread.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,14 @@
33
use std::cell::RefCell;
44
use std::collections::hash_map::Entry;
55
use std::num::TryFromIntError;
6+
use std::sync::atomic::AtomicU64;
7+
use std::sync::Arc;
68
use std::time::{Duration, Instant, SystemTime};
79

810
use log::trace;
911

1012
use rustc_data_structures::fx::FxHashMap;
13+
use rustc_data_structures::sync::Ordering;
1114
use rustc_hir::def_id::DefId;
1215
use rustc_index::vec::{Idx, IndexVec};
1316
use rustc_middle::mir::Mutability;
@@ -16,6 +19,7 @@ use rustc_target::spec::abi::Abi;
1619

1720
use crate::concurrency::data_race;
1821
use crate::concurrency::sync::SynchronizationState;
22+
use crate::shims::time::TimeAnchor;
1923
use crate::*;
2024

2125
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@@ -184,6 +188,7 @@ impl<'mir, 'tcx> Thread<'mir, 'tcx> {
184188
#[derive(Debug)]
185189
pub enum Time {
186190
Monotonic(Instant),
191+
Virtual(u64, Arc<AtomicU64>),
187192
RealTime(SystemTime),
188193
}
189194

@@ -192,6 +197,8 @@ impl Time {
192197
fn get_wait_time(&self) -> Duration {
193198
match self {
194199
Time::Monotonic(instant) => instant.saturating_duration_since(Instant::now()),
200+
Time::Virtual(nanoseconds, now) =>
201+
Duration::from_nanos(nanoseconds - now.load(Ordering::Relaxed)),
195202
Time::RealTime(time) =>
196203
time.duration_since(SystemTime::now()).unwrap_or(Duration::new(0, 0)),
197204
}
@@ -234,6 +241,8 @@ pub struct ThreadManager<'mir, 'tcx> {
234241
yield_active_thread: bool,
235242
/// Callbacks that are called once the specified time passes.
236243
timeout_callbacks: FxHashMap<ThreadId, TimeoutCallbackInfo<'mir, 'tcx>>,
244+
/// The "time anchor" for this machine's monotone clock (for `Instant` simulation).
245+
pub(crate) time_anchor: TimeAnchor,
237246
}
238247

239248
impl<'mir, 'tcx> Default for ThreadManager<'mir, 'tcx> {
@@ -248,6 +257,7 @@ impl<'mir, 'tcx> Default for ThreadManager<'mir, 'tcx> {
248257
thread_local_alloc_ids: Default::default(),
249258
yield_active_thread: false,
250259
timeout_callbacks: FxHashMap::default(),
260+
time_anchor: TimeAnchor::new(false),
251261
}
252262
}
253263
}
@@ -258,6 +268,8 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
258268
// The main thread can *not* be joined on except on windows.
259269
ecx.machine.threads.threads[ThreadId::new(0)].join_status = ThreadJoinStatus::Detached;
260270
}
271+
272+
ecx.machine.threads.time_anchor = TimeAnchor::new(ecx.machine.communicate());
261273
}
262274

263275
/// Check if we have an allocation for the given thread local static for the
@@ -611,7 +623,8 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
611623
// All threads are currently blocked, but we have unexecuted
612624
// timeout_callbacks, which may unblock some of the threads. Hence,
613625
// sleep until the first callback.
614-
std::thread::sleep(sleep_time);
626+
627+
self.time_anchor.sleep(sleep_time);
615628
Ok(SchedulingAction::ExecuteTimeoutCallback)
616629
} else {
617630
throw_machine_stop!(TerminationInfo::Deadlock);

src/eval.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -344,11 +344,6 @@ pub fn eval_entry<'tcx>(
344344
assert!(ecx.step()?, "a terminated thread was scheduled for execution");
345345
}
346346
SchedulingAction::ExecuteTimeoutCallback => {
347-
assert!(
348-
ecx.machine.communicate(),
349-
"scheduler callbacks require disabled isolation, but the code \
350-
that created the callback did not check it"
351-
);
352347
ecx.run_timeout_callback()?;
353348
}
354349
SchedulingAction::ExecuteDtors => {

src/machine.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
use std::borrow::Cow;
55
use std::cell::RefCell;
66
use std::fmt;
7-
use std::time::Instant;
87

98
use rand::rngs::StdRng;
109
use rand::SeedableRng;
@@ -294,9 +293,6 @@ pub struct Evaluator<'mir, 'tcx> {
294293
/// The table of directory descriptors.
295294
pub(crate) dir_handler: shims::unix::DirHandler,
296295

297-
/// The "time anchor" for this machine's monotone clock (for `Instant` simulation).
298-
pub(crate) time_anchor: Instant,
299-
300296
/// The set of threads.
301297
pub(crate) threads: ThreadManager<'mir, 'tcx>,
302298

@@ -392,7 +388,6 @@ impl<'mir, 'tcx> Evaluator<'mir, 'tcx> {
392388
enforce_abi: config.check_abi,
393389
file_handler: FileHandler::new(config.mute_stdout_stderr),
394390
dir_handler: Default::default(),
395-
time_anchor: Instant::now(),
396391
layouts,
397392
threads: ThreadManager::default(),
398393
static_roots: Vec::new(),
@@ -961,6 +956,9 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> {
961956
}
962957
// These are our preemption points.
963958
ecx.maybe_preempt_active_thread();
959+
960+
ecx.machine.threads.time_anchor.tick();
961+
964962
Ok(())
965963
}
966964

src/shims/time.rs

Lines changed: 74 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,68 @@
1+
use std::sync::atomic::AtomicU64;
2+
use std::sync::Arc;
13
use std::time::{Duration, Instant, SystemTime};
24

5+
use rustc_data_structures::sync::Ordering;
6+
37
use crate::concurrency::thread::Time;
48
use crate::*;
59

10+
#[derive(Debug)]
11+
pub enum TimeAnchor {
12+
Host(Instant),
13+
Virtual(Arc<AtomicU64>),
14+
}
15+
16+
impl TimeAnchor {
17+
pub fn new(communicate: bool) -> Self {
18+
if communicate { Self::Host(Instant::now()) } else { Self::Virtual(Arc::new(0.into())) }
19+
}
20+
21+
pub fn get(&self) -> Duration {
22+
match self {
23+
Self::Host(instant) => Instant::now().saturating_duration_since(*instant),
24+
Self::Virtual(nanoseconds) => Duration::from_nanos(nanoseconds.load(Ordering::Relaxed)),
25+
}
26+
}
27+
28+
pub fn tick(&self) {
29+
match self {
30+
Self::Host(_) => (),
31+
Self::Virtual(nanoseconds) => {
32+
nanoseconds.fetch_add(1, Ordering::Relaxed);
33+
}
34+
}
35+
}
36+
37+
pub fn sleep(&self, duration: Duration) {
38+
match self {
39+
Self::Host(_) => std::thread::sleep(duration),
40+
Self::Virtual(nanoseconds) => {
41+
nanoseconds.fetch_add(duration.as_nanos().try_into().unwrap(), Ordering::Relaxed);
42+
}
43+
}
44+
}
45+
46+
pub fn checked_add_since_now(&self, duration: Duration) -> Option<Time> {
47+
match self {
48+
Self::Host(_) => Instant::now().checked_add(duration).map(Time::Monotonic),
49+
Self::Virtual(nanoseconds) =>
50+
nanoseconds
51+
.load(Ordering::Relaxed)
52+
.checked_add(duration.as_nanos().try_into().unwrap())
53+
.map(|duration| Time::Virtual(duration, nanoseconds.clone())),
54+
}
55+
}
56+
57+
pub fn checked_add_since_start(&self, duration: Duration) -> Option<Time> {
58+
match self {
59+
Self::Host(instant) => instant.checked_add(duration).map(Time::Monotonic),
60+
Self::Virtual(now) =>
61+
Some(Time::Virtual(duration.as_nanos().try_into().unwrap(), now.clone())),
62+
}
63+
}
64+
}
65+
666
/// Returns the time elapsed between the provided time and the unix epoch as a `Duration`.
767
pub fn system_time_to_duration<'tcx>(time: &SystemTime) -> InterpResult<'tcx, Duration> {
868
time.duration_since(SystemTime::UNIX_EPOCH)
@@ -23,7 +83,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
2383
let this = self.eval_context_mut();
2484

2585
this.assert_target_os("linux", "clock_gettime");
26-
this.check_no_isolation("`clock_gettime`")?;
2786

2887
let clk_id = this.read_scalar(clk_id_op)?.to_i32()?;
2988

@@ -40,9 +99,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
4099
[this.eval_libc_i32("CLOCK_MONOTONIC")?, this.eval_libc_i32("CLOCK_MONOTONIC_COARSE")?];
41100

42101
let duration = if absolute_clocks.contains(&clk_id) {
102+
this.check_no_isolation("`clock_gettime` with real time clocks")?;
43103
system_time_to_duration(&SystemTime::now())?
44104
} else if relative_clocks.contains(&clk_id) {
45-
Instant::now().duration_since(this.machine.time_anchor)
105+
this.machine.threads.time_anchor.get()
46106
} else {
47107
let einval = this.eval_libc("EINVAL")?;
48108
this.set_last_error(einval)?;
@@ -127,7 +187,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
127187

128188
// QueryPerformanceCounter uses a hardware counter as its basis.
129189
// Miri will emulate a counter with a resolution of 1 nanosecond.
130-
let duration = Instant::now().duration_since(this.machine.time_anchor);
190+
let duration = this.machine.threads.time_anchor.get();
131191
let qpc = i64::try_from(duration.as_nanos()).map_err(|_| {
132192
err_unsup_format!("programs running longer than 2^63 nanoseconds are not supported")
133193
})?;
@@ -168,7 +228,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
168228

169229
// This returns a u64, with time units determined dynamically by `mach_timebase_info`.
170230
// We return plain nanoseconds.
171-
let duration = Instant::now().duration_since(this.machine.time_anchor);
231+
let duration = this.machine.threads.time_anchor.get();
172232
let res = u64::try_from(duration.as_nanos()).map_err(|_| {
173233
err_unsup_format!("programs running longer than 2^64 nanoseconds are not supported")
174234
})?;
@@ -202,7 +262,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
202262
let this = self.eval_context_mut();
203263

204264
this.assert_target_os_is_unix("nanosleep");
205-
this.check_no_isolation("`nanosleep`")?;
206265

207266
let duration = match this.read_timespec(&this.deref_operand(req_op)?)? {
208267
Some(duration) => duration,
@@ -213,10 +272,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
213272
}
214273
};
215274
// If adding the duration overflows, let's just sleep for an hour. Waking up early is always acceptable.
216-
let timeout_time = Instant::now()
217-
.checked_add(duration)
218-
.unwrap_or_else(|| Instant::now().checked_add(Duration::from_secs(3600)).unwrap());
219-
let timeout_time = Time::Monotonic(timeout_time);
275+
let timeout_time =
276+
this.machine.threads.time_anchor.checked_add_since_now(duration).unwrap_or_else(|| {
277+
this.machine
278+
.threads
279+
.time_anchor
280+
.checked_add_since_now(Duration::from_secs(3600))
281+
.unwrap()
282+
});
220283

221284
let active_thread = this.get_active_thread();
222285
this.block_thread(active_thread);
@@ -238,12 +301,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
238301
let this = self.eval_context_mut();
239302

240303
this.assert_target_os("windows", "Sleep");
241-
this.check_no_isolation("`Sleep`")?;
242304

243305
let timeout_ms = this.read_scalar(timeout)?.to_u32()?;
244306

245307
let duration = Duration::from_millis(timeout_ms.into());
246-
let timeout_time = Time::Monotonic(Instant::now().checked_add(duration).unwrap());
308+
let timeout_time =
309+
this.machine.threads.time_anchor.checked_add_since_now(duration).unwrap();
247310

248311
let active_thread = this.get_active_thread();
249312
this.block_thread(active_thread);

src/shims/unix/linux/sync.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::concurrency::thread::Time;
22
use crate::*;
33
use rustc_target::abi::{Align, Size};
4-
use std::time::{Instant, SystemTime};
4+
use std::time::SystemTime;
55

66
/// Implementation of the SYS_futex syscall.
77
/// `args` is the arguments *after* the syscall number.
@@ -106,14 +106,14 @@ pub fn futex<'tcx>(
106106
if op & futex_realtime != 0 {
107107
Time::RealTime(SystemTime::UNIX_EPOCH.checked_add(duration).unwrap())
108108
} else {
109-
Time::Monotonic(this.machine.time_anchor.checked_add(duration).unwrap())
109+
this.machine.threads.time_anchor.checked_add_since_start(duration).unwrap()
110110
}
111111
} else {
112112
// FUTEX_WAIT uses a relative timestamp.
113113
if op & futex_realtime != 0 {
114114
Time::RealTime(SystemTime::now().checked_add(duration).unwrap())
115115
} else {
116-
Time::Monotonic(Instant::now().checked_add(duration).unwrap())
116+
this.machine.threads.time_anchor.checked_add_since_now(duration).unwrap()
117117
}
118118
})
119119
};

src/shims/unix/sync.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -859,7 +859,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
859859
let timeout_time = if clock_id == this.eval_libc_i32("CLOCK_REALTIME")? {
860860
Time::RealTime(SystemTime::UNIX_EPOCH.checked_add(duration).unwrap())
861861
} else if clock_id == this.eval_libc_i32("CLOCK_MONOTONIC")? {
862-
Time::Monotonic(this.machine.time_anchor.checked_add(duration).unwrap())
862+
this.machine.threads.time_anchor.checked_add_since_start(duration).unwrap()
863863
} else {
864864
throw_unsup_format!("unsupported clock id: {}", clock_id);
865865
};

0 commit comments

Comments
 (0)