Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 6a37643

Browse files
committed
make sleep work with isolation enabled
1 parent 7e66a9f commit 6a37643

File tree

7 files changed

+199
-60
lines changed

7 files changed

+199
-60
lines changed

src/concurrency/thread.rs

Lines changed: 25 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use std::cell::RefCell;
44
use std::collections::hash_map::Entry;
55
use std::num::TryFromIntError;
6-
use std::time::{Duration, Instant, SystemTime};
6+
use std::time::{Duration, SystemTime};
77

88
use log::trace;
99

@@ -16,6 +16,7 @@ use rustc_target::spec::abi::Abi;
1616

1717
use crate::concurrency::data_race;
1818
use crate::concurrency::sync::SynchronizationState;
19+
use crate::shims::time::{Clock, Instant};
1920
use crate::*;
2021

2122
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@@ -187,17 +188,6 @@ pub enum Time {
187188
RealTime(SystemTime),
188189
}
189190

190-
impl Time {
191-
/// How long do we have to wait from now until the specified time?
192-
fn get_wait_time(&self) -> Duration {
193-
match self {
194-
Time::Monotonic(instant) => instant.saturating_duration_since(Instant::now()),
195-
Time::RealTime(time) =>
196-
time.duration_since(SystemTime::now()).unwrap_or(Duration::new(0, 0)),
197-
}
198-
}
199-
}
200-
201191
/// Callbacks are used to implement timeouts. For example, waiting on a
202192
/// conditional variable with a timeout creates a callback that is called after
203193
/// the specified time and unblocks the thread. If another thread signals on the
@@ -490,13 +480,16 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
490480
}
491481

492482
/// Get a callback that is ready to be called.
493-
fn get_ready_callback(&mut self) -> Option<(ThreadId, TimeoutCallback<'mir, 'tcx>)> {
483+
fn get_ready_callback(
484+
&mut self,
485+
clock: &Clock,
486+
) -> Option<(ThreadId, TimeoutCallback<'mir, 'tcx>)> {
494487
// We iterate over all threads in the order of their indices because
495488
// this allows us to have a deterministic scheduler.
496489
for thread in self.threads.indices() {
497490
match self.timeout_callbacks.entry(thread) {
498491
Entry::Occupied(entry) =>
499-
if entry.get().call_time.get_wait_time() == Duration::new(0, 0) {
492+
if clock.get_wait_time(&entry.get().call_time) == Duration::new(0, 0) {
500493
return Some((thread, entry.remove().callback));
501494
},
502495
Entry::Vacant(_) => {}
@@ -553,7 +546,7 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
553546
/// used in stateless model checkers such as Loom: run the active thread as
554547
/// long as we can and switch only when we have to (the active thread was
555548
/// blocked, terminated, or has explicitly asked to be preempted).
556-
fn schedule(&mut self) -> InterpResult<'tcx, SchedulingAction> {
549+
fn schedule(&mut self, clock: &Clock) -> InterpResult<'tcx, SchedulingAction> {
557550
// Check whether the thread has **just** terminated (`check_terminated`
558551
// checks whether the thread has popped all its stack and if yes, sets
559552
// the thread state to terminated).
@@ -580,7 +573,7 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
580573
// at the time of the call".
581574
// <https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_timedwait.html>
582575
let potential_sleep_time =
583-
self.timeout_callbacks.values().map(|info| info.call_time.get_wait_time()).min();
576+
self.timeout_callbacks.values().map(|info| clock.get_wait_time(&info.call_time)).min();
584577
if potential_sleep_time == Some(Duration::new(0, 0)) {
585578
return Ok(SchedulingAction::ExecuteTimeoutCallback);
586579
}
@@ -615,7 +608,8 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
615608
// All threads are currently blocked, but we have unexecuted
616609
// timeout_callbacks, which may unblock some of the threads. Hence,
617610
// sleep until the first callback.
618-
std::thread::sleep(sleep_time);
611+
612+
clock.sleep(sleep_time);
619613
Ok(SchedulingAction::ExecuteTimeoutCallback)
620614
} else {
621615
throw_machine_stop!(TerminationInfo::Deadlock);
@@ -878,18 +872,19 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
878872
#[inline]
879873
fn run_timeout_callback(&mut self) -> InterpResult<'tcx> {
880874
let this = self.eval_context_mut();
881-
let (thread, callback) =
882-
if let Some((thread, callback)) = this.machine.threads.get_ready_callback() {
883-
(thread, callback)
884-
} else {
885-
// get_ready_callback can return None if the computer's clock
886-
// was shifted after calling the scheduler and before the call
887-
// to get_ready_callback (see issue
888-
// https://github.com/rust-lang/miri/issues/1763). In this case,
889-
// just do nothing, which effectively just returns to the
890-
// scheduler.
891-
return Ok(());
892-
};
875+
let (thread, callback) = if let Some((thread, callback)) =
876+
this.machine.threads.get_ready_callback(&this.machine.clock)
877+
{
878+
(thread, callback)
879+
} else {
880+
// get_ready_callback can return None if the computer's clock
881+
// was shifted after calling the scheduler and before the call
882+
// to get_ready_callback (see issue
883+
// https://github.com/rust-lang/miri/issues/1763). In this case,
884+
// just do nothing, which effectively just returns to the
885+
// scheduler.
886+
return Ok(());
887+
};
893888
// This back-and-forth with `set_active_thread` is here because of two
894889
// design decisions:
895890
// 1. Make the caller and not the callback responsible for changing
@@ -906,7 +901,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
906901
#[inline]
907902
fn schedule(&mut self) -> InterpResult<'tcx, SchedulingAction> {
908903
let this = self.eval_context_mut();
909-
this.machine.threads.schedule()
904+
this.machine.threads.schedule(&this.machine.clock)
910905
}
911906

912907
/// Handles thread termination of the active thread: wakes up threads joining on this one,

src/eval.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -359,11 +359,6 @@ pub fn eval_entry<'tcx>(
359359
assert!(ecx.step()?, "a terminated thread was scheduled for execution");
360360
}
361361
SchedulingAction::ExecuteTimeoutCallback => {
362-
assert!(
363-
ecx.machine.communicate(),
364-
"scheduler callbacks require disabled isolation, but the code \
365-
that created the callback did not check it"
366-
);
367362
ecx.run_timeout_callback()?;
368363
}
369364
SchedulingAction::ExecuteDtors => {

src/machine.rs

Lines changed: 7 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;
@@ -28,7 +27,7 @@ use rustc_target::spec::abi::Abi;
2827

2928
use crate::{
3029
concurrency::{data_race, weak_memory},
31-
shims::unix::FileHandler,
30+
shims::{time::Clock, unix::FileHandler},
3231
*,
3332
};
3433

@@ -327,8 +326,8 @@ pub struct Evaluator<'mir, 'tcx> {
327326
/// The table of directory descriptors.
328327
pub(crate) dir_handler: shims::unix::DirHandler,
329328

330-
/// The "time anchor" for this machine's monotone clock (for `Instant` simulation).
331-
pub(crate) time_anchor: Instant,
329+
/// This machine's monotone clock.
330+
pub(crate) clock: Clock,
332331

333332
/// The set of threads.
334333
pub(crate) threads: ThreadManager<'mir, 'tcx>,
@@ -434,7 +433,6 @@ impl<'mir, 'tcx> Evaluator<'mir, 'tcx> {
434433
enforce_abi: config.check_abi,
435434
file_handler: FileHandler::new(config.mute_stdout_stderr),
436435
dir_handler: Default::default(),
437-
time_anchor: Instant::now(),
438436
layouts,
439437
threads: ThreadManager::default(),
440438
static_roots: Vec::new(),
@@ -454,6 +452,7 @@ impl<'mir, 'tcx> Evaluator<'mir, 'tcx> {
454452
preemption_rate: config.preemption_rate,
455453
report_progress: config.report_progress,
456454
basic_block_count: 0,
455+
clock: Clock::new(config.isolated_op == IsolatedOp::Allow),
457456
external_so_lib: config.external_so_file.as_ref().map(|lib_file_path| {
458457
// Check if host target == the session target.
459458
if env!("TARGET") != target_triple {
@@ -1036,6 +1035,9 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> {
10361035

10371036
// These are our preemption points.
10381037
ecx.maybe_preempt_active_thread();
1038+
1039+
ecx.machine.clock.tick();
1040+
10391041
Ok(())
10401042
}
10411043

0 commit comments

Comments
 (0)