@@ -298,8 +298,18 @@ pub mod panic_count {
298298
299299 pub const ALWAYS_ABORT_FLAG : usize = 1 << ( usize:: BITS - 1 ) ;
300300
301- // Panic count for the current thread.
302- thread_local ! { static LOCAL_PANIC_COUNT : Cell <usize > = const { Cell :: new( 0 ) } }
301+ /// A reason for forcing an immediate abort on panic.
302+ #[ derive( Debug ) ]
303+ pub enum MustAbort {
304+ AlwaysAbort ,
305+ PanicInHook ,
306+ }
307+
308+ // Panic count for the current thread and whether a panic hook is currently
309+ // being executed..
310+ thread_local ! {
311+ static LOCAL_PANIC_COUNT : Cell <( usize , bool ) > = const { Cell :: new( ( 0 , false ) ) }
312+ }
303313
304314 // Sum of panic counts from all threads. The purpose of this is to have
305315 // a fast path in `count_is_zero` (which is used by `panicking`). In any particular
@@ -328,34 +338,39 @@ pub mod panic_count {
328338 // panicking thread consumes at least 2 bytes of address space.
329339 static GLOBAL_PANIC_COUNT : AtomicUsize = AtomicUsize :: new ( 0 ) ;
330340
331- // Return the state of the ALWAYS_ABORT_FLAG and number of panics.
341+ // Increases the global and local panic count, and returns whether an
342+ // immediate abort is required.
332343 //
333- // If ALWAYS_ABORT_FLAG is not set, the number is determined on a per-thread
334- // base (stored in LOCAL_PANIC_COUNT), i.e. it is the amount of recursive calls
335- // of the calling thread.
336- // If ALWAYS_ABORT_FLAG is set, the number equals the *global* number of panic
337- // calls. See above why LOCAL_PANIC_COUNT is not used.
338- pub fn increase ( ) -> ( bool , usize ) {
344+ // This also updates thread-local state to keep track of whether a panic
345+ // hook is currently executing.
346+ pub fn increase ( run_panic_hook : bool ) -> Option < MustAbort > {
339347 let global_count = GLOBAL_PANIC_COUNT . fetch_add ( 1 , Ordering :: Relaxed ) ;
340- let must_abort = global_count & ALWAYS_ABORT_FLAG != 0 ;
341- let panics = if must_abort {
342- global_count & !ALWAYS_ABORT_FLAG
343- } else {
344- LOCAL_PANIC_COUNT . with ( |c| {
345- let next = c. get ( ) + 1 ;
346- c. set ( next) ;
347- next
348- } )
349- } ;
350- ( must_abort, panics)
348+ if global_count & ALWAYS_ABORT_FLAG != 0 {
349+ return Some ( MustAbort :: AlwaysAbort ) ;
350+ }
351+
352+ LOCAL_PANIC_COUNT . with ( |c| {
353+ let ( count, in_panic_hook) = c. get ( ) ;
354+ if in_panic_hook {
355+ return Some ( MustAbort :: PanicInHook ) ;
356+ }
357+ c. set ( ( count + 1 , run_panic_hook) ) ;
358+ None
359+ } )
360+ }
361+
362+ pub fn finished_panic_hook ( ) {
363+ LOCAL_PANIC_COUNT . with ( |c| {
364+ let ( count, _) = c. get ( ) ;
365+ c. set ( ( count, false ) ) ;
366+ } ) ;
351367 }
352368
353369 pub fn decrease ( ) {
354370 GLOBAL_PANIC_COUNT . fetch_sub ( 1 , Ordering :: Relaxed ) ;
355371 LOCAL_PANIC_COUNT . with ( |c| {
356- let next = c. get ( ) - 1 ;
357- c. set ( next) ;
358- next
372+ let ( count, _) = c. get ( ) ;
373+ c. set ( ( count - 1 , false ) ) ;
359374 } ) ;
360375 }
361376
@@ -366,7 +381,7 @@ pub mod panic_count {
366381 // Disregards ALWAYS_ABORT_FLAG
367382 #[ must_use]
368383 pub fn get_count ( ) -> usize {
369- LOCAL_PANIC_COUNT . with ( |c| c. get ( ) )
384+ LOCAL_PANIC_COUNT . with ( |c| c. get ( ) . 0 )
370385 }
371386
372387 // Disregards ALWAYS_ABORT_FLAG
@@ -394,7 +409,7 @@ pub mod panic_count {
394409 #[ inline( never) ]
395410 #[ cold]
396411 fn is_zero_slow_path ( ) -> bool {
397- LOCAL_PANIC_COUNT . with ( |c| c. get ( ) == 0 )
412+ LOCAL_PANIC_COUNT . with ( |c| c. get ( ) . 0 == 0 )
398413 }
399414}
400415
@@ -655,23 +670,22 @@ fn rust_panic_with_hook(
655670 location : & Location < ' _ > ,
656671 can_unwind : bool ,
657672) -> ! {
658- let ( must_abort, panics) = panic_count:: increase ( ) ;
659-
660- // If this is the third nested call (e.g., panics == 2, this is 0-indexed),
661- // the panic hook probably triggered the last panic, otherwise the
662- // double-panic check would have aborted the process. In this case abort the
663- // process real quickly as we don't want to try calling it again as it'll
664- // probably just panic again.
665- if must_abort || panics > 2 {
666- if panics > 2 {
667- // Don't try to print the message in this case
668- // - perhaps that is causing the recursive panics.
669- rtprintpanic ! ( "thread panicked while processing panic. aborting.\n " ) ;
670- } else {
671- // Unfortunately, this does not print a backtrace, because creating
672- // a `Backtrace` will allocate, which we must to avoid here.
673- let panicinfo = PanicInfo :: internal_constructor ( message, location, can_unwind) ;
674- rtprintpanic ! ( "{panicinfo}\n panicked after panic::always_abort(), aborting.\n " ) ;
673+ let must_abort = panic_count:: increase ( true ) ;
674+
675+ // Check if we need to abort immediately.
676+ if let Some ( must_abort) = must_abort {
677+ match must_abort {
678+ panic_count:: MustAbort :: PanicInHook => {
679+ // Don't try to print the message in this case
680+ // - perhaps that is causing the recursive panics.
681+ rtprintpanic ! ( "thread panicked while processing panic. aborting.\n " ) ;
682+ }
683+ panic_count:: MustAbort :: AlwaysAbort => {
684+ // Unfortunately, this does not print a backtrace, because creating
685+ // a `Backtrace` will allocate, which we must to avoid here.
686+ let panicinfo = PanicInfo :: internal_constructor ( message, location, can_unwind) ;
687+ rtprintpanic ! ( "{panicinfo}\n panicked after panic::always_abort(), aborting.\n " ) ;
688+ }
675689 }
676690 crate :: sys:: abort_internal ( ) ;
677691 }
@@ -697,16 +711,16 @@ fn rust_panic_with_hook(
697711 } ;
698712 drop ( hook) ;
699713
700- if panics > 1 || !can_unwind {
701- // If a thread panics while it's already unwinding then we
702- // have limited options. Currently our preference is to
703- // just abort. In the future we may consider resuming
704- // unwinding or otherwise exiting the thread cleanly.
705- if !can_unwind {
706- rtprintpanic ! ( " thread caused non-unwinding panic. aborting. \n " ) ;
707- } else {
708- rtprintpanic ! ( "thread panicked while panicking. aborting. \n " ) ;
709- }
714+ // Indicate that we have finished executing the panic hook. After this point
715+ // it is fine if there is a panic while executing destructors, as long as it
716+ // it contained within a `catch_unwind`.
717+ panic_count :: finished_panic_hook ( ) ;
718+
719+ if !can_unwind {
720+ // If a thread panics while running destructors or tries to unwind
721+ // through a nounwind function (e.g. extern "C") then we cannot continue
722+ // unwinding and have to abort immediately.
723+ rtprintpanic ! ( "thread caused non-unwinding panic. aborting. \n " ) ;
710724 crate :: sys:: abort_internal ( ) ;
711725 }
712726
@@ -716,7 +730,7 @@ fn rust_panic_with_hook(
716730/// This is the entry point for `resume_unwind`.
717731/// It just forwards the payload to the panic runtime.
718732pub fn rust_panic_without_hook ( payload : Box < dyn Any + Send > ) -> ! {
719- panic_count:: increase ( ) ;
733+ panic_count:: increase ( false ) ;
720734
721735 struct RewrapBox ( Box < dyn Any + Send > ) ;
722736
0 commit comments