Introduce a new detect_nondeterminism = true
attribute to gpui::test
This commit is contained in:
parent
f0a721032d
commit
fa3f100eff
3 changed files with 150 additions and 33 deletions
|
@ -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) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue