Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EXPERIMENT: support well-nested unwinds #99260

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 39 additions & 5 deletions library/std/src/panicking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,35 @@ pub mod panic_count {
});
}

// Decrease the panic count only if the thread is already panicking.
// Allows running code nested within a wind that may itself unwind.
#[inline]
#[must_use]
pub fn try_decrease() -> bool {
if GLOBAL_PANIC_COUNT.load(Ordering::Relaxed) & !ALWAYS_ABORT_FLAG == 0 {
// Fast path: see count_is_zero.
false
} else {
try_decrease_slow_path()
}
}

// We consider unwinding to be rare, so mark this function as cold.
// However, leave the inlining decision entirely to the optimizer.
#[cold]
fn try_decrease_slow_path() -> bool {
LOCAL_PANIC_COUNT.with(|c| {
let panic_count = c.get();
if panic_count > 0 {
GLOBAL_PANIC_COUNT.fetch_sub(1, Ordering::Relaxed);
c.set(panic_count - 1);
true
} else {
false
}
})
}

pub fn set_always_abort() {
GLOBAL_PANIC_COUNT.fetch_or(ALWAYS_ABORT_FLAG, Ordering::Relaxed);
}
Expand Down Expand Up @@ -453,7 +482,12 @@ pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>>
// - `do_catch`, the second argument, can be called with the `data_ptr` as well.
// See their safety preconditions for more information
unsafe {
return if intrinsics::r#try(do_call::<F, R>, data_ptr, do_catch::<F, R>) == 0 {
let is_panicking = panic_count::try_decrease();
let success = intrinsics::r#try(do_call::<F, R>, data_ptr, do_catch::<F, R>) == 0;
if is_panicking {
panic_count::increase();
}
return if success {
Ok(ManuallyDrop::into_inner(data.r))
} else {
Err(ManuallyDrop::into_inner(data.p))
Expand Down Expand Up @@ -705,10 +739,10 @@ fn rust_panic_with_hook(
}

if panics > 1 || !can_unwind {
// If a thread panics while it's already unwinding then we
// have limited options. Currently our preference is to
// just abort. In the future we may consider resuming
// unwinding or otherwise exiting the thread cleanly.
// If the thread was already panicking, then the closest catch_unwind
// has already been claimed by the existing panic. If a new catch_unwind
// had been registered, it would have cleared the panicking flag. Since
// this panic is not well-nested, we just abort the process.
rtprintpanic!("thread panicked while panicking. aborting.\n");
crate::sys::abort_internal();
}
Expand Down
18 changes: 18 additions & 0 deletions src/test/ui/panics/well-nested-panic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// run-pass
// needs-unwind

use std::panic::catch_unwind;

struct Bomb;
impl Drop for Bomb {
fn drop(&mut self) {
let _ = catch_unwind(|| panic!("bomb"));
}
}

fn main() {
let _ = catch_unwind(|| {
let _bomb = Bomb;
panic!("main");
});
}