Skip to content

Commit ccb4141

Browse files
authored
Better error messages for usages of ExecutionState::with (#219)
1 parent 1599566 commit ccb4141

File tree

4 files changed

+46
-10
lines changed

4 files changed

+46
-10
lines changed

shuttle/src/current.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,12 +87,15 @@ pub fn set_name_for_task(task_id: TaskId, task_name: impl Into<TaskName>) -> Opt
8787
// when running with something like the `tracing_subscriber::fmt` subscriber.
8888
// This either has to be lived with, or the task name should be set via the `ChildLabelFn` mechanism, or a different subscriber should be used
8989
// (if this is done, then `record_steps_in_span` should be set to true as well), or Shuttle will have to be chanegd to recreate the Span
90-
ExecutionState::try_with(|state| {
90+
let res = ExecutionState::try_with(|state| {
9191
state
9292
.get_mut(task_id)
9393
.step_span
9494
.record("task", format!("{task_name:?}"));
9595
});
96+
if let Err(e) = res {
97+
tracing::error!("`set_name_for_task` failed with error: {e:?}");
98+
}
9699
set_label_for_task::<TaskName>(task_id, task_name)
97100
}
98101

shuttle/src/future/mod.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,14 @@ pub struct AbortHandle {
6161
impl AbortHandle {
6262
/// Abort the task associated with the handle.
6363
pub fn abort(&self) {
64-
ExecutionState::try_with(|state| {
64+
let res = ExecutionState::try_with(|state| {
6565
if !state.is_finished() {
6666
state.get_mut(self.task_id).abort();
6767
}
6868
});
69+
if let Err(e) = res {
70+
tracing::error!("`AbortHandle::abort` failed with error: {e:?}");
71+
}
6972
}
7073

7174
/// Returns `true` if this task is finished, otherwise returns `false`.
@@ -108,11 +111,14 @@ impl<T> Default for JoinHandleInner<T> {
108111
impl<T> JoinHandle<T> {
109112
/// Abort the task associated with the handle.
110113
pub fn abort(&self) {
111-
ExecutionState::try_with(|state| {
114+
let res = ExecutionState::try_with(|state| {
112115
if !state.is_finished() {
113116
state.get_mut(self.task_id).abort();
114117
}
115118
});
119+
if let Err(e) = res {
120+
tracing::error!("`JoinHandle::abort` failed with error: {e:?}");
121+
}
116122
}
117123

118124
/// Returns `true` if this task is finished, otherwise returns `false`.

shuttle/src/runtime/execution.rs

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,15 @@ impl ScheduledTask {
363363
}
364364
}
365365

366+
/// Error type for when an `ExecutionState::with` fails
367+
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
368+
pub enum ExecutionStateBorrowError {
369+
/// `ExecutionState` is currently not set
370+
NotSet,
371+
/// We are trying to borrow `ExecutionState` while it is already borrowed
372+
AlreadyBorrowed,
373+
}
374+
366375
impl ExecutionState {
367376
fn new(config: Config, scheduler: Rc<RefCell<dyn Scheduler>>) -> Self {
368377
Self {
@@ -387,30 +396,46 @@ impl ExecutionState {
387396
/// access to the state of the execution to influence scheduling (e.g. to register a task as
388397
/// blocked).
389398
#[inline]
399+
#[track_caller]
390400
pub(crate) fn with<F, T>(f: F) -> T
391401
where
392402
F: FnOnce(&mut ExecutionState) -> T,
393403
{
394-
Self::try_with(f).expect("Shuttle internal error: cannot access ExecutionState. are you trying to access a Shuttle primitive from outside a Shuttle test?")
404+
Self::try_with(f).unwrap_or_else(|e| {
405+
eprintln!("`ExecutionState::try_with` failed with error: {e:?}");
406+
eprintln!(
407+
"Backtrace for `with`: {:#?}",
408+
std::backtrace::Backtrace::force_capture()
409+
);
410+
match e {
411+
ExecutionStateBorrowError::AlreadyBorrowed => panic!("`ExecutionState::with` panicked because `ExecutionState` is already borrowed."),
412+
ExecutionStateBorrowError::NotSet => panic!("`ExecutionState::with` panicked because `ExecutionState` is not set. Are you accessing a Shuttle primitive outside of a Shuttle test?"),
413+
}
414+
})
395415
}
396416

397417
/// Like `with`, but returns None instead of panicking if there is no current ExecutionState or
398418
/// if the current ExecutionState is already borrowed.
399419
#[inline]
400-
pub(crate) fn try_with<F, T>(f: F) -> Option<T>
420+
#[track_caller]
421+
pub(crate) fn try_with<F, T>(f: F) -> Result<T, ExecutionStateBorrowError>
401422
where
402423
F: FnOnce(&mut ExecutionState) -> T,
403424
{
425+
trace!(
426+
"ExecutionState::try_with called from {:?}",
427+
std::panic::Location::caller()
428+
);
404429
if EXECUTION_STATE.is_set() {
405430
EXECUTION_STATE.with(|cell| {
406431
if let Ok(mut state) = cell.try_borrow_mut() {
407-
Some(f(&mut state))
432+
Ok(f(&mut state))
408433
} else {
409-
None
434+
Err(ExecutionStateBorrowError::AlreadyBorrowed)
410435
}
411436
})
412437
} else {
413-
None
438+
Err(ExecutionStateBorrowError::NotSet)
414439
}
415440
}
416441

@@ -717,7 +742,7 @@ impl ExecutionState {
717742
"<unknown>".into()
718743
}
719744
})
720-
.unwrap_or_else(|| "ExecutionState is either not set or currently borrowed. This should not happen".to_string())
745+
.unwrap_or_else(|e| format!("Tried to get ExecutionState, but got the following error: {e:?}"))
721746
}
722747

723748
/// Generate a random u64 from the current scheduler and return it.

shuttle/tests/basic/execution.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,9 @@ fn context_switches_mutex() {
240240
/// Check that we get a good failure message if accessing a Shuttle primitive from outside an
241241
/// execution.
242242
#[test]
243-
#[should_panic(expected = "are you trying to access a Shuttle primitive from outside a Shuttle test?")]
243+
#[should_panic(
244+
expected = "`ExecutionState::with` panicked because `ExecutionState` is not set. Are you accessing a Shuttle primitive outside of a Shuttle test?"
245+
)]
244246
fn failure_outside_execution() {
245247
let lock = shuttle::sync::Mutex::new(0u64);
246248
let _ = lock.lock().unwrap();

0 commit comments

Comments
 (0)