in src/runtime/execution.rs [82:169]
fn step(&mut self, config: &Config) -> bool {
enum NextStep {
Task(Rc<RefCell<PooledContinuation>>),
Failure(String, Schedule),
Finished,
}
let next_step = ExecutionState::with(|state| {
if let Err(msg) = state.schedule() {
return NextStep::Failure(msg, state.current_schedule.clone());
}
state.advance_to_next_task();
match state.current_task {
ScheduledTask::Some(tid) => {
let task = state.get(tid);
NextStep::Task(Rc::clone(&task.continuation))
}
ScheduledTask::Finished => {
// The scheduler decided we're finished, so there are either no runnable tasks,
// or all runnable tasks are detached and there are no unfinished attached
// tasks. Therefore, it's a deadlock if there are unfinished attached tasks.
if state.tasks.iter().any(|t| !t.finished() && !t.detached) {
let blocked_tasks = state
.tasks
.iter()
.filter(|t| !t.finished())
.map(|t| {
format!(
"{} (task {}{}{})",
t.name().unwrap_or_else(|| "<unknown>".to_string()),
t.id().0,
if t.detached { ", detached" } else { "" },
if t.sleeping() { ", pending future" } else { "" },
)
})
.collect::<Vec<_>>();
NextStep::Failure(
format!("deadlock! blocked tasks: [{}]", blocked_tasks.join(", ")),
state.current_schedule.clone(),
)
} else {
NextStep::Finished
}
}
ScheduledTask::Stopped => NextStep::Finished,
ScheduledTask::None => {
NextStep::Failure("no task was scheduled".to_string(), state.current_schedule.clone())
}
}
});
// Run a single step of the chosen task.
let ret = match next_step {
NextStep::Task(continuation) => {
panic::catch_unwind(panic::AssertUnwindSafe(|| continuation.borrow_mut().resume()))
}
NextStep::Failure(msg, schedule) => {
// Because we're creating the panic here, we don't need `persist_failure` to print
// as the failure message will be part of the panic payload.
let message = persist_failure(&schedule, msg, config, false);
panic!("{}", message);
}
NextStep::Finished => return false,
};
match ret {
// Task finished
Ok(true) => {
ExecutionState::with(|state| state.current_mut().finish());
}
// Task yielded
Ok(false) => {}
// Task failed
Err(e) => {
let (name, schedule) = ExecutionState::failure_info().unwrap();
let message = persist_task_failure(&schedule, name, config, true);
// Try to inject the schedule into the panic payload if we can
let payload: Box<dyn Any + Send> = match e.downcast::<String>() {
Ok(panic_msg) => Box::new(format!("{}\noriginal panic: {}", message, panic_msg)),
Err(panic) => panic,
};
panic::resume_unwind(payload);
}
}
true
}