@@ -180,7 +180,7 @@ pub fn take_hook() -> Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send> {
180180fn default_hook ( info : & PanicInfo < ' _ > ) {
181181 // If this is a double panic, make sure that we print a backtrace
182182 // for this panic. Otherwise only print it if logging is enabled.
183- let backtrace_env = if panic_count:: get ( ) >= 2 {
183+ let backtrace_env = if panic_count:: get_count ( ) >= 2 {
184184 RustBacktrace :: Print ( crate :: backtrace_rs:: PrintFmt :: Full )
185185 } else {
186186 backtrace:: rust_backtrace_env ( )
@@ -233,6 +233,8 @@ pub mod panic_count {
233233 use crate :: cell:: Cell ;
234234 use crate :: sync:: atomic:: { AtomicUsize , Ordering } ;
235235
236+ pub const ALWAYS_ABORT_FLAG : usize = 1 << ( usize:: BITS - 1 ) ;
237+
236238 // Panic count for the current thread.
237239 thread_local ! { static LOCAL_PANIC_COUNT : Cell <usize > = Cell :: new( 0 ) }
238240
@@ -241,15 +243,29 @@ pub mod panic_count {
241243 // thread, if that thread currently views `GLOBAL_PANIC_COUNT` as being zero,
242244 // then `LOCAL_PANIC_COUNT` in that thread is zero. This invariant holds before
243245 // and after increase and decrease, but not necessarily during their execution.
246+ //
247+ // Additionally, the top bit of GLOBAL_PANIC_COUNT (GLOBAL_ALWAYS_ABORT_FLAG)
248+ // records whether panic::always_abort() has been called. This can only be
249+ // set, never cleared.
250+ //
251+ // This could be viewed as a struct containing a single bit and an n-1-bit
252+ // value, but if we wrote it like that it would be more than a single word,
253+ // and even a newtype around usize would be clumsy because we need atomics.
254+ // But we use such a tuple for the return type of increase().
255+ //
256+ // Stealing a bit is fine because it just amounts to assuming that each
257+ // panicking thread consumes at least 2 bytes of address space.
244258 static GLOBAL_PANIC_COUNT : AtomicUsize = AtomicUsize :: new ( 0 ) ;
245259
246- pub fn increase ( ) -> usize {
247- GLOBAL_PANIC_COUNT . fetch_add ( 1 , Ordering :: Relaxed ) ;
248- LOCAL_PANIC_COUNT . with ( |c| {
249- let next = c. get ( ) + 1 ;
250- c. set ( next) ;
251- next
252- } )
260+ pub fn increase ( ) -> ( bool , usize ) {
261+ (
262+ GLOBAL_PANIC_COUNT . fetch_add ( 1 , Ordering :: Acquire ) & ALWAYS_ABORT_FLAG != 0 ,
263+ LOCAL_PANIC_COUNT . with ( |c| {
264+ let next = c. get ( ) + 1 ;
265+ c. set ( next) ;
266+ next
267+ } ) ,
268+ )
253269 }
254270
255271 pub fn decrease ( ) {
@@ -261,13 +277,19 @@ pub mod panic_count {
261277 } ) ;
262278 }
263279
264- pub fn get ( ) -> usize {
280+ pub fn set_always_abort ( ) {
281+ GLOBAL_PANIC_COUNT . fetch_or ( ALWAYS_ABORT_FLAG , Ordering :: Release ) ;
282+ }
283+
284+ // Disregards ALWAYS_ABORT_FLAG
285+ pub fn get_count ( ) -> usize {
265286 LOCAL_PANIC_COUNT . with ( |c| c. get ( ) )
266287 }
267288
289+ // Disregards ALWAYS_ABORT_FLAG
268290 #[ inline]
269- pub fn is_zero ( ) -> bool {
270- if GLOBAL_PANIC_COUNT . load ( Ordering :: Relaxed ) == 0 {
291+ pub fn count_is_zero ( ) -> bool {
292+ if GLOBAL_PANIC_COUNT . load ( Ordering :: Relaxed ) & ! ALWAYS_ABORT_FLAG == 0 {
271293 // Fast path: if `GLOBAL_PANIC_COUNT` is zero, all threads
272294 // (including the current one) will have `LOCAL_PANIC_COUNT`
273295 // equal to zero, so TLS access can be avoided.
@@ -410,7 +432,7 @@ pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>>
410432/// Determines whether the current thread is unwinding because of panic.
411433#[ inline]
412434pub fn panicking ( ) -> bool {
413- !panic_count:: is_zero ( )
435+ !panic_count:: count_is_zero ( )
414436}
415437
416438/// The entry point for panicking with a formatted message.
@@ -563,15 +585,27 @@ fn rust_panic_with_hook(
563585 message : Option < & fmt:: Arguments < ' _ > > ,
564586 location : & Location < ' _ > ,
565587) -> ! {
566- let panics = panic_count:: increase ( ) ;
588+ let ( must_abort , panics) = panic_count:: increase ( ) ;
567589
568590 // If this is the third nested call (e.g., panics == 2, this is 0-indexed),
569591 // the panic hook probably triggered the last panic, otherwise the
570592 // double-panic check would have aborted the process. In this case abort the
571593 // process real quickly as we don't want to try calling it again as it'll
572594 // probably just panic again.
573- if panics > 2 {
574- util:: dumb_print ( format_args ! ( "thread panicked while processing panic. aborting.\n " ) ) ;
595+ if must_abort || panics > 2 {
596+ if panics > 2 {
597+ // Don't try to print the message in this case
598+ // - perhaps that is causing the recursive panics.
599+ util:: dumb_print ( format_args ! ( "thread panicked while processing panic. aborting.\n " ) ) ;
600+ } else {
601+ // Unfortunately, this does not print a backtrace, because creating
602+ // a `Backtrace` will allocate, which we must to avoid here.
603+ let panicinfo = PanicInfo :: internal_constructor ( message, location) ;
604+ util:: dumb_print ( format_args ! (
605+ "{}\n panicked after panic::always_abort(), aborting.\n " ,
606+ panicinfo
607+ ) ) ;
608+ }
575609 intrinsics:: abort ( )
576610 }
577611
0 commit comments