@@ -298,8 +298,18 @@ pub mod panic_count {
298
298
299
299
pub const ALWAYS_ABORT_FLAG : usize = 1 << ( usize:: BITS - 1 ) ;
300
300
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
+ }
303
313
304
314
// Sum of panic counts from all threads. The purpose of this is to have
305
315
// a fast path in `count_is_zero` (which is used by `panicking`). In any particular
@@ -328,34 +338,39 @@ pub mod panic_count {
328
338
// panicking thread consumes at least 2 bytes of address space.
329
339
static GLOBAL_PANIC_COUNT : AtomicUsize = AtomicUsize :: new ( 0 ) ;
330
340
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.
332
343
//
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 > {
339
347
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
+ } ) ;
351
367
}
352
368
353
369
pub fn decrease ( ) {
354
370
GLOBAL_PANIC_COUNT . fetch_sub ( 1 , Ordering :: Relaxed ) ;
355
371
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 ) ) ;
359
374
} ) ;
360
375
}
361
376
@@ -366,7 +381,7 @@ pub mod panic_count {
366
381
// Disregards ALWAYS_ABORT_FLAG
367
382
#[ must_use]
368
383
pub fn get_count ( ) -> usize {
369
- LOCAL_PANIC_COUNT . with ( |c| c. get ( ) )
384
+ LOCAL_PANIC_COUNT . with ( |c| c. get ( ) . 0 )
370
385
}
371
386
372
387
// Disregards ALWAYS_ABORT_FLAG
@@ -394,7 +409,7 @@ pub mod panic_count {
394
409
#[ inline( never) ]
395
410
#[ cold]
396
411
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 )
398
413
}
399
414
}
400
415
@@ -655,23 +670,22 @@ fn rust_panic_with_hook(
655
670
location : & Location < ' _ > ,
656
671
can_unwind : bool ,
657
672
) -> ! {
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
+ }
675
689
}
676
690
crate :: sys:: abort_internal ( ) ;
677
691
}
@@ -697,16 +711,16 @@ fn rust_panic_with_hook(
697
711
} ;
698
712
drop ( hook) ;
699
713
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 " ) ;
710
724
crate :: sys:: abort_internal ( ) ;
711
725
}
712
726
@@ -716,7 +730,7 @@ fn rust_panic_with_hook(
716
730
/// This is the entry point for `resume_unwind`.
717
731
/// It just forwards the payload to the panic runtime.
718
732
pub fn rust_panic_without_hook ( payload : Box < dyn Any + Send > ) -> ! {
719
- panic_count:: increase ( ) ;
733
+ panic_count:: increase ( false ) ;
720
734
721
735
struct RewrapBox ( Box < dyn Any + Send > ) ;
722
736
0 commit comments