Report backtraces of pending conditions when deterministic executor illegally parks
Co-Authored-By: Max Brunsfeld <maxbrunsfeld@gmail.com>
This commit is contained in:
parent
2dbee1d914
commit
943571af2a
3 changed files with 108 additions and 58 deletions
|
@ -77,6 +77,7 @@ struct DeterministicState {
|
|||
block_on_ticks: RangeInclusive<usize>,
|
||||
now: Instant,
|
||||
pending_timers: Vec<(Instant, barrier::Sender)>,
|
||||
waiting_backtrace: Option<Backtrace>,
|
||||
}
|
||||
|
||||
pub struct Deterministic {
|
||||
|
@ -97,6 +98,7 @@ impl Deterministic {
|
|||
block_on_ticks: 0..=1000,
|
||||
now: Instant::now(),
|
||||
pending_timers: Default::default(),
|
||||
waiting_backtrace: None,
|
||||
})),
|
||||
parker: Default::default(),
|
||||
}
|
||||
|
@ -143,8 +145,8 @@ impl Deterministic {
|
|||
return result;
|
||||
}
|
||||
|
||||
if !woken.load(SeqCst) && self.state.lock().forbid_parking {
|
||||
panic!("deterministic executor parked after a call to forbid_parking");
|
||||
if !woken.load(SeqCst) {
|
||||
self.state.lock().will_park();
|
||||
}
|
||||
|
||||
woken.store(false, SeqCst);
|
||||
|
@ -206,7 +208,11 @@ impl Deterministic {
|
|||
}
|
||||
|
||||
let state = self.state.lock();
|
||||
if state.would_park() {
|
||||
|
||||
if state.scheduled_from_foreground.is_empty()
|
||||
&& state.scheduled_from_background.is_empty()
|
||||
&& state.spawned_from_foreground.is_empty()
|
||||
{
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
@ -241,11 +247,9 @@ impl Deterministic {
|
|||
if let Poll::Ready(result) = future.as_mut().poll(&mut cx) {
|
||||
return Some(result);
|
||||
}
|
||||
let state = self.state.lock();
|
||||
let mut state = self.state.lock();
|
||||
if state.scheduled_from_background.is_empty() {
|
||||
if state.forbid_parking {
|
||||
panic!("deterministic executor parked after a call to forbid_parking");
|
||||
}
|
||||
state.will_park();
|
||||
drop(state);
|
||||
self.parker.lock().park();
|
||||
}
|
||||
|
@ -259,11 +263,22 @@ impl Deterministic {
|
|||
}
|
||||
|
||||
impl DeterministicState {
|
||||
fn would_park(&self) -> bool {
|
||||
self.forbid_parking
|
||||
&& self.scheduled_from_foreground.is_empty()
|
||||
&& self.scheduled_from_background.is_empty()
|
||||
&& self.spawned_from_foreground.is_empty()
|
||||
fn will_park(&mut self) {
|
||||
if self.forbid_parking {
|
||||
let mut backtrace_message = String::new();
|
||||
if let Some(backtrace) = self.waiting_backtrace.as_mut() {
|
||||
backtrace.resolve();
|
||||
backtrace_message = format!(
|
||||
"\nbacktrace of waiting future:\n{:?}",
|
||||
CwdBacktrace::new(backtrace)
|
||||
);
|
||||
}
|
||||
|
||||
panic!(
|
||||
"deterministic executor parked after a call to forbid_parking{}",
|
||||
backtrace_message
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -312,32 +327,53 @@ impl Trace {
|
|||
}
|
||||
}
|
||||
|
||||
impl Debug for Trace {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
struct FirstCwdFrameInBacktrace<'a>(&'a Backtrace);
|
||||
struct CwdBacktrace<'a> {
|
||||
backtrace: &'a Backtrace,
|
||||
first_frame_only: bool,
|
||||
}
|
||||
|
||||
impl<'a> Debug for FirstCwdFrameInBacktrace<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let cwd = std::env::current_dir().unwrap();
|
||||
let mut print_path = |fmt: &mut fmt::Formatter<'_>, path: BytesOrWideString<'_>| {
|
||||
fmt::Display::fmt(&path, fmt)
|
||||
};
|
||||
let mut fmt = BacktraceFmt::new(f, backtrace::PrintFmt::Full, &mut print_path);
|
||||
for frame in self.0.frames() {
|
||||
let mut formatted_frame = fmt.frame();
|
||||
if frame
|
||||
.symbols()
|
||||
.iter()
|
||||
.any(|s| s.filename().map_or(false, |f| f.starts_with(&cwd)))
|
||||
{
|
||||
formatted_frame.backtrace_frame(frame)?;
|
||||
break;
|
||||
}
|
||||
impl<'a> CwdBacktrace<'a> {
|
||||
fn new(backtrace: &'a Backtrace) -> Self {
|
||||
Self {
|
||||
backtrace,
|
||||
first_frame_only: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn first_frame(backtrace: &'a Backtrace) -> Self {
|
||||
Self {
|
||||
backtrace,
|
||||
first_frame_only: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Debug for CwdBacktrace<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let cwd = std::env::current_dir().unwrap();
|
||||
let mut print_path = |fmt: &mut fmt::Formatter<'_>, path: BytesOrWideString<'_>| {
|
||||
fmt::Display::fmt(&path, fmt)
|
||||
};
|
||||
let mut fmt = BacktraceFmt::new(f, backtrace::PrintFmt::Full, &mut print_path);
|
||||
for frame in self.backtrace.frames() {
|
||||
let mut formatted_frame = fmt.frame();
|
||||
if frame
|
||||
.symbols()
|
||||
.iter()
|
||||
.any(|s| s.filename().map_or(false, |f| f.starts_with(&cwd)))
|
||||
{
|
||||
formatted_frame.backtrace_frame(frame)?;
|
||||
if self.first_frame_only {
|
||||
break;
|
||||
}
|
||||
fmt.finish()
|
||||
}
|
||||
}
|
||||
fmt.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Trace {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
for ((backtrace, scheduled), spawned_from_foreground) in self
|
||||
.executed
|
||||
.iter()
|
||||
|
@ -346,7 +382,7 @@ impl Debug for Trace {
|
|||
{
|
||||
writeln!(f, "Scheduled")?;
|
||||
for backtrace in scheduled {
|
||||
writeln!(f, "- {:?}", FirstCwdFrameInBacktrace(backtrace))?;
|
||||
writeln!(f, "- {:?}", CwdBacktrace::first_frame(backtrace))?;
|
||||
}
|
||||
if scheduled.is_empty() {
|
||||
writeln!(f, "None")?;
|
||||
|
@ -355,14 +391,14 @@ impl Debug for Trace {
|
|||
|
||||
writeln!(f, "Spawned from foreground")?;
|
||||
for backtrace in spawned_from_foreground {
|
||||
writeln!(f, "- {:?}", FirstCwdFrameInBacktrace(backtrace))?;
|
||||
writeln!(f, "- {:?}", CwdBacktrace::first_frame(backtrace))?;
|
||||
}
|
||||
if spawned_from_foreground.is_empty() {
|
||||
writeln!(f, "None")?;
|
||||
}
|
||||
writeln!(f, "==========")?;
|
||||
|
||||
writeln!(f, "Run: {:?}", FirstCwdFrameInBacktrace(backtrace))?;
|
||||
writeln!(f, "Run: {:?}", CwdBacktrace::first_frame(backtrace))?;
|
||||
writeln!(f, "+++++++++++++++++++")?;
|
||||
}
|
||||
|
||||
|
@ -446,9 +482,20 @@ impl Foreground {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn would_park(&self) -> bool {
|
||||
pub fn start_waiting(&self) {
|
||||
match self {
|
||||
Self::Deterministic(executor) => executor.state.lock().would_park(),
|
||||
Self::Deterministic(executor) => {
|
||||
executor.state.lock().waiting_backtrace = Some(Backtrace::new_unresolved());
|
||||
}
|
||||
_ => panic!("this method can only be called on a deterministic executor"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn finish_waiting(&self) {
|
||||
match self {
|
||||
Self::Deterministic(executor) => {
|
||||
executor.state.lock().waiting_backtrace.take();
|
||||
}
|
||||
_ => panic!("this method can only be called on a deterministic executor"),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue