Introduce a new detect_nondeterminism = true attribute to gpui::test

This commit is contained in:
Antonio Scandurra 2022-11-28 19:01:28 +01:00
parent f0a721032d
commit fa3f100eff
3 changed files with 150 additions and 33 deletions

View file

@ -66,21 +66,31 @@ struct DeterministicState {
rng: rand::prelude::StdRng,
seed: u64,
scheduled_from_foreground: collections::HashMap<usize, Vec<ForegroundRunnable>>,
scheduled_from_background: Vec<Runnable>,
scheduled_from_background: Vec<BackgroundRunnable>,
forbid_parking: bool,
block_on_ticks: std::ops::RangeInclusive<usize>,
now: std::time::Instant,
next_timer_id: usize,
pending_timers: Vec<(usize, std::time::Instant, postage::barrier::Sender)>,
waiting_backtrace: Option<backtrace::Backtrace>,
next_runnable_id: usize,
poll_history: Vec<usize>,
runnable_backtraces: collections::HashMap<usize, backtrace::Backtrace>,
}
#[cfg(any(test, feature = "test-support"))]
struct ForegroundRunnable {
id: usize,
runnable: Runnable,
main: bool,
}
#[cfg(any(test, feature = "test-support"))]
struct BackgroundRunnable {
id: usize,
runnable: Runnable,
}
#[cfg(any(test, feature = "test-support"))]
pub struct Deterministic {
state: Arc<parking_lot::Mutex<DeterministicState>>,
@ -117,11 +127,24 @@ impl Deterministic {
next_timer_id: Default::default(),
pending_timers: Default::default(),
waiting_backtrace: None,
next_runnable_id: 0,
poll_history: Default::default(),
runnable_backtraces: Default::default(),
})),
parker: Default::default(),
})
}
pub fn runnable_history(&self) -> Vec<usize> {
self.state.lock().poll_history.clone()
}
pub fn runnable_backtrace(&self, runnable_id: usize) -> backtrace::Backtrace {
let mut backtrace = self.state.lock().runnable_backtraces[&runnable_id].clone();
backtrace.resolve();
backtrace
}
pub fn build_background(self: &Arc<Self>) -> Arc<Background> {
Arc::new(Background::Deterministic {
executor: self.clone(),
@ -142,6 +165,15 @@ impl Deterministic {
main: bool,
) -> AnyLocalTask {
let state = self.state.clone();
let id;
{
let mut state = state.lock();
id = util::post_inc(&mut state.next_runnable_id);
state
.runnable_backtraces
.insert(id, backtrace::Backtrace::new_unresolved());
}
let unparker = self.parker.lock().unparker();
let (runnable, task) = async_task::spawn_local(future, move |runnable| {
let mut state = state.lock();
@ -149,7 +181,7 @@ impl Deterministic {
.scheduled_from_foreground
.entry(cx_id)
.or_default()
.push(ForegroundRunnable { runnable, main });
.push(ForegroundRunnable { id, runnable, main });
unparker.unpark();
});
runnable.schedule();
@ -158,10 +190,21 @@ impl Deterministic {
fn spawn(&self, future: AnyFuture) -> AnyTask {
let state = self.state.clone();
let id;
{
let mut state = state.lock();
id = util::post_inc(&mut state.next_runnable_id);
state
.runnable_backtraces
.insert(id, backtrace::Backtrace::new_unresolved());
}
let unparker = self.parker.lock().unparker();
let (runnable, task) = async_task::spawn(future, move |runnable| {
let mut state = state.lock();
state.scheduled_from_background.push(runnable);
state
.scheduled_from_background
.push(BackgroundRunnable { id, runnable });
unparker.unpark();
});
runnable.schedule();
@ -178,15 +221,25 @@ impl Deterministic {
let woken = Arc::new(AtomicBool::new(false));
let state = self.state.clone();
let id;
{
let mut state = state.lock();
id = util::post_inc(&mut state.next_runnable_id);
state
.runnable_backtraces
.insert(id, backtrace::Backtrace::new());
}
let unparker = self.parker.lock().unparker();
let (runnable, mut main_task) = unsafe {
async_task::spawn_unchecked(main_future, move |runnable| {
let mut state = state.lock();
let state = &mut *state.lock();
state
.scheduled_from_foreground
.entry(cx_id)
.or_default()
.push(ForegroundRunnable {
id: util::post_inc(&mut state.next_runnable_id),
runnable,
main: true,
});
@ -248,9 +301,10 @@ impl Deterministic {
if !state.scheduled_from_background.is_empty() && state.rng.gen() {
let background_len = state.scheduled_from_background.len();
let ix = state.rng.gen_range(0..background_len);
let runnable = state.scheduled_from_background.remove(ix);
let background_runnable = state.scheduled_from_background.remove(ix);
state.poll_history.push(background_runnable.id);
drop(state);
runnable.run();
background_runnable.runnable.run();
} else if !state.scheduled_from_foreground.is_empty() {
let available_cx_ids = state
.scheduled_from_foreground
@ -266,6 +320,7 @@ impl Deterministic {
if scheduled_from_cx.is_empty() {
state.scheduled_from_foreground.remove(&cx_id_to_run);
}
state.poll_history.push(foreground_runnable.id);
drop(state);
@ -298,9 +353,10 @@ impl Deterministic {
let runnable_count = state.scheduled_from_background.len();
let ix = state.rng.gen_range(0..=runnable_count);
if ix < state.scheduled_from_background.len() {
let runnable = state.scheduled_from_background.remove(ix);
let background_runnable = state.scheduled_from_background.remove(ix);
state.poll_history.push(background_runnable.id);
drop(state);
runnable.run();
background_runnable.runnable.run();
} else {
drop(state);
if let Poll::Ready(result) = future.poll(&mut cx) {