mirror of
https://github.com/zed-industries/zed.git
synced 2025-01-12 21:32:40 +00:00
Don't poll foreground futures during DeterministicExecutor::block_on
This commit is contained in:
parent
416571e8e0
commit
502f54a021
1 changed files with 79 additions and 27 deletions
|
@ -44,7 +44,8 @@ pub enum Background {
|
||||||
struct DeterministicState {
|
struct DeterministicState {
|
||||||
rng: StdRng,
|
rng: StdRng,
|
||||||
seed: u64,
|
seed: u64,
|
||||||
scheduled: Vec<(Runnable, Backtrace)>,
|
scheduled_from_foreground: Vec<(Runnable, Backtrace)>,
|
||||||
|
scheduled_from_background: Vec<(Runnable, Backtrace)>,
|
||||||
spawned_from_foreground: Vec<(Runnable, Backtrace)>,
|
spawned_from_foreground: Vec<(Runnable, Backtrace)>,
|
||||||
forbid_parking: bool,
|
forbid_parking: bool,
|
||||||
block_on_ticks: RangeInclusive<usize>,
|
block_on_ticks: RangeInclusive<usize>,
|
||||||
|
@ -61,7 +62,8 @@ impl Deterministic {
|
||||||
state: Arc::new(Mutex::new(DeterministicState {
|
state: Arc::new(Mutex::new(DeterministicState {
|
||||||
rng: StdRng::seed_from_u64(seed),
|
rng: StdRng::seed_from_u64(seed),
|
||||||
seed,
|
seed,
|
||||||
scheduled: Default::default(),
|
scheduled_from_foreground: Default::default(),
|
||||||
|
scheduled_from_background: Default::default(),
|
||||||
spawned_from_foreground: Default::default(),
|
spawned_from_foreground: Default::default(),
|
||||||
forbid_parking: false,
|
forbid_parking: false,
|
||||||
block_on_ticks: 0..=1000,
|
block_on_ticks: 0..=1000,
|
||||||
|
@ -83,7 +85,7 @@ impl Deterministic {
|
||||||
let mut state = state.lock();
|
let mut state = state.lock();
|
||||||
let backtrace = backtrace.clone();
|
let backtrace = backtrace.clone();
|
||||||
if scheduled_once.fetch_or(true, SeqCst) {
|
if scheduled_once.fetch_or(true, SeqCst) {
|
||||||
state.scheduled.push((runnable, backtrace));
|
state.scheduled_from_foreground.push((runnable, backtrace));
|
||||||
} else {
|
} else {
|
||||||
state.spawned_from_foreground.push((runnable, backtrace));
|
state.spawned_from_foreground.push((runnable, backtrace));
|
||||||
}
|
}
|
||||||
|
@ -103,7 +105,9 @@ impl Deterministic {
|
||||||
let unparker = self.parker.lock().unparker();
|
let unparker = self.parker.lock().unparker();
|
||||||
let (runnable, task) = async_task::spawn(future, move |runnable| {
|
let (runnable, task) = async_task::spawn(future, move |runnable| {
|
||||||
let mut state = state.lock();
|
let mut state = state.lock();
|
||||||
state.scheduled.push((runnable, backtrace.clone()));
|
state
|
||||||
|
.scheduled_from_background
|
||||||
|
.push((runnable, backtrace.clone()));
|
||||||
unparker.unpark();
|
unparker.unpark();
|
||||||
});
|
});
|
||||||
runnable.schedule();
|
runnable.schedule();
|
||||||
|
@ -114,14 +118,6 @@ impl Deterministic {
|
||||||
where
|
where
|
||||||
T: 'static,
|
T: 'static,
|
||||||
F: Future<Output = T> + 'static,
|
F: Future<Output = T> + 'static,
|
||||||
{
|
|
||||||
self.block_on(usize::MAX, future).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn block_on<F, T>(&self, max_ticks: usize, future: F) -> Option<T>
|
|
||||||
where
|
|
||||||
T: 'static,
|
|
||||||
F: Future<Output = T>,
|
|
||||||
{
|
{
|
||||||
smol::pin!(future);
|
smol::pin!(future);
|
||||||
|
|
||||||
|
@ -132,14 +128,26 @@ impl Deterministic {
|
||||||
|
|
||||||
let mut cx = Context::from_waker(&waker);
|
let mut cx = Context::from_waker(&waker);
|
||||||
let mut trace = Trace::default();
|
let mut trace = Trace::default();
|
||||||
for _ in 0..max_ticks {
|
loop {
|
||||||
let mut state = self.state.lock();
|
let mut state = self.state.lock();
|
||||||
let runnable_count = state.scheduled.len() + state.spawned_from_foreground.len();
|
let runnable_count = state.scheduled_from_foreground.len()
|
||||||
|
+ state.scheduled_from_background.len()
|
||||||
|
+ state.spawned_from_foreground.len();
|
||||||
|
|
||||||
let ix = state.rng.gen_range(0..=runnable_count);
|
let ix = state.rng.gen_range(0..=runnable_count);
|
||||||
if ix < state.scheduled.len() {
|
if ix < state.scheduled_from_foreground.len() {
|
||||||
let (_, backtrace) = &state.scheduled[ix];
|
let (_, backtrace) = &state.scheduled_from_foreground[ix];
|
||||||
trace.record(&state, backtrace.clone());
|
trace.record(&state, backtrace.clone());
|
||||||
let runnable = state.scheduled.remove(ix).0;
|
let runnable = state.scheduled_from_foreground.remove(ix).0;
|
||||||
|
drop(state);
|
||||||
|
runnable.run();
|
||||||
|
} else if ix - state.scheduled_from_foreground.len()
|
||||||
|
< state.scheduled_from_background.len()
|
||||||
|
{
|
||||||
|
let ix = ix - state.scheduled_from_foreground.len();
|
||||||
|
let (_, backtrace) = &state.scheduled_from_background[ix];
|
||||||
|
trace.record(&state, backtrace.clone());
|
||||||
|
let runnable = state.scheduled_from_background.remove(ix).0;
|
||||||
drop(state);
|
drop(state);
|
||||||
runnable.run();
|
runnable.run();
|
||||||
} else if ix < runnable_count {
|
} else if ix < runnable_count {
|
||||||
|
@ -148,13 +156,64 @@ impl Deterministic {
|
||||||
let runnable = state.spawned_from_foreground.remove(0).0;
|
let runnable = state.spawned_from_foreground.remove(0).0;
|
||||||
drop(state);
|
drop(state);
|
||||||
runnable.run();
|
runnable.run();
|
||||||
|
} else {
|
||||||
|
drop(state);
|
||||||
|
if let Poll::Ready(result) = future.as_mut().poll(&mut cx) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
let state = self.state.lock();
|
||||||
|
if state.scheduled_from_foreground.is_empty()
|
||||||
|
&& state.scheduled_from_background.is_empty()
|
||||||
|
&& state.spawned_from_foreground.is_empty()
|
||||||
|
{
|
||||||
|
if state.forbid_parking {
|
||||||
|
panic!("deterministic executor parked after a call to forbid_parking");
|
||||||
|
}
|
||||||
|
drop(state);
|
||||||
|
self.parker.lock().park();
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn block_on<F, T>(&self, future: F) -> Option<T>
|
||||||
|
where
|
||||||
|
T: 'static,
|
||||||
|
F: Future<Output = T>,
|
||||||
|
{
|
||||||
|
smol::pin!(future);
|
||||||
|
|
||||||
|
let unparker = self.parker.lock().unparker();
|
||||||
|
let waker = waker_fn(move || {
|
||||||
|
unparker.unpark();
|
||||||
|
});
|
||||||
|
let max_ticks = {
|
||||||
|
let mut state = self.state.lock();
|
||||||
|
let range = state.block_on_ticks.clone();
|
||||||
|
state.rng.gen_range(range)
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut cx = Context::from_waker(&waker);
|
||||||
|
let mut trace = Trace::default();
|
||||||
|
for _ in 0..max_ticks {
|
||||||
|
let mut state = self.state.lock();
|
||||||
|
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 (_, backtrace) = &state.scheduled_from_background[ix];
|
||||||
|
trace.record(&state, backtrace.clone());
|
||||||
|
let runnable = state.scheduled_from_background.remove(ix).0;
|
||||||
|
drop(state);
|
||||||
|
runnable.run();
|
||||||
} else {
|
} else {
|
||||||
drop(state);
|
drop(state);
|
||||||
if let Poll::Ready(result) = future.as_mut().poll(&mut cx) {
|
if let Poll::Ready(result) = future.as_mut().poll(&mut cx) {
|
||||||
return Some(result);
|
return Some(result);
|
||||||
}
|
}
|
||||||
let state = self.state.lock();
|
let state = self.state.lock();
|
||||||
if state.scheduled.is_empty() && state.spawned_from_foreground.is_empty() {
|
if state.scheduled_from_background.is_empty() {
|
||||||
if state.forbid_parking {
|
if state.forbid_parking {
|
||||||
panic!("deterministic executor parked after a call to forbid_parking");
|
panic!("deterministic executor parked after a call to forbid_parking");
|
||||||
}
|
}
|
||||||
|
@ -181,7 +240,7 @@ impl Trace {
|
||||||
fn record(&mut self, state: &DeterministicState, executed: Backtrace) {
|
fn record(&mut self, state: &DeterministicState, executed: Backtrace) {
|
||||||
self.scheduled.push(
|
self.scheduled.push(
|
||||||
state
|
state
|
||||||
.scheduled
|
.scheduled_from_foreground
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(_, backtrace)| backtrace.clone())
|
.map(|(_, backtrace)| backtrace.clone())
|
||||||
.collect(),
|
.collect(),
|
||||||
|
@ -394,14 +453,7 @@ impl Background {
|
||||||
Self::Production { .. } => {
|
Self::Production { .. } => {
|
||||||
smol::block_on(util::timeout(timeout, Pin::new(&mut future))).ok()
|
smol::block_on(util::timeout(timeout, Pin::new(&mut future))).ok()
|
||||||
}
|
}
|
||||||
Self::Deterministic(executor) => {
|
Self::Deterministic(executor) => executor.block_on(Pin::new(&mut future)),
|
||||||
let max_ticks = {
|
|
||||||
let mut state = executor.state.lock();
|
|
||||||
let range = state.block_on_ticks.clone();
|
|
||||||
state.rng.gen_range(range)
|
|
||||||
};
|
|
||||||
executor.block_on(max_ticks, Pin::new(&mut future))
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(output) = output {
|
if let Some(output) = output {
|
||||||
|
|
Loading…
Reference in a new issue