Skip to content

Commit 9c2f009

Browse files
committed
Support well-nested unwinds
1 parent c2f428d commit 9c2f009

File tree

2 files changed

+57
-5
lines changed

2 files changed

+57
-5
lines changed

library/std/src/panicking.rs

+39-5
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,35 @@ pub mod panic_count {
366366
});
367367
}
368368

369+
// Decrease the panic count only if the thread is already panicking.
370+
// Allows running code nested within a wind that may itself unwind.
371+
#[inline]
372+
#[must_use]
373+
pub fn try_decrease() -> bool {
374+
if GLOBAL_PANIC_COUNT.load(Ordering::Relaxed) & !ALWAYS_ABORT_FLAG == 0 {
375+
// Fast path: see count_is_zero.
376+
false
377+
} else {
378+
try_decrease_slow_path()
379+
}
380+
}
381+
382+
// We consider unwinding to be rare, so mark this function as cold.
383+
// However, leave the inlining decision entirely to the optimizer.
384+
#[cold]
385+
fn try_decrease_slow_path() -> bool {
386+
LOCAL_PANIC_COUNT.with(|c| {
387+
let panic_count = c.get();
388+
if panic_count > 0 {
389+
GLOBAL_PANIC_COUNT.fetch_sub(1, Ordering::Relaxed);
390+
c.set(panic_count - 1);
391+
true
392+
} else {
393+
false
394+
}
395+
})
396+
}
397+
369398
pub fn set_always_abort() {
370399
GLOBAL_PANIC_COUNT.fetch_or(ALWAYS_ABORT_FLAG, Ordering::Relaxed);
371400
}
@@ -453,7 +482,12 @@ pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>>
453482
// - `do_catch`, the second argument, can be called with the `data_ptr` as well.
454483
// See their safety preconditions for more information
455484
unsafe {
456-
return if intrinsics::r#try(do_call::<F, R>, data_ptr, do_catch::<F, R>) == 0 {
485+
let is_panicking = panic_count::try_decrease();
486+
let success = intrinsics::r#try(do_call::<F, R>, data_ptr, do_catch::<F, R>) == 0;
487+
if is_panicking {
488+
panic_count::increase();
489+
}
490+
return if success {
457491
Ok(ManuallyDrop::into_inner(data.r))
458492
} else {
459493
Err(ManuallyDrop::into_inner(data.p))
@@ -705,10 +739,10 @@ fn rust_panic_with_hook(
705739
}
706740

707741
if panics > 1 || !can_unwind {
708-
// If a thread panics while it's already unwinding then we
709-
// have limited options. Currently our preference is to
710-
// just abort. In the future we may consider resuming
711-
// unwinding or otherwise exiting the thread cleanly.
742+
// If the thread was already panicking, then the closest catch_unwind
743+
// has already been claimed by the existing panic. If a new catch_unwind
744+
// had been registered, it would have cleared the panicking flag. Since
745+
// this panic is not well-nested, we just abort the process.
712746
rtprintpanic!("thread panicked while panicking. aborting.\n");
713747
crate::sys::abort_internal();
714748
}
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// run-pass
2+
// needs-unwind
3+
4+
use std::panic::catch_unwind;
5+
6+
struct Bomb;
7+
impl Drop for Bomb {
8+
fn drop(&mut self) {
9+
let _ = catch_unwind(|| panic!("bomb"));
10+
}
11+
}
12+
13+
fn main() {
14+
let _ = catch_unwind(|| {
15+
let _bomb = Bomb;
16+
panic!("main");
17+
});
18+
}

0 commit comments

Comments
 (0)