@@ -180,7 +180,7 @@ pub fn take_hook() -> Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send> {
180
180
fn default_hook ( info : & PanicInfo < ' _ > ) {
181
181
// If this is a double panic, make sure that we print a backtrace
182
182
// 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 {
184
184
RustBacktrace :: Print ( crate :: backtrace_rs:: PrintFmt :: Full )
185
185
} else {
186
186
backtrace:: rust_backtrace_env ( )
@@ -233,6 +233,8 @@ pub mod panic_count {
233
233
use crate :: cell:: Cell ;
234
234
use crate :: sync:: atomic:: { AtomicUsize , Ordering } ;
235
235
236
+ pub const ALWAYS_ABORT_FLAG : usize = 1 << ( usize:: BITS - 1 ) ;
237
+
236
238
// Panic count for the current thread.
237
239
thread_local ! { static LOCAL_PANIC_COUNT : Cell <usize > = Cell :: new( 0 ) }
238
240
@@ -241,15 +243,29 @@ pub mod panic_count {
241
243
// thread, if that thread currently views `GLOBAL_PANIC_COUNT` as being zero,
242
244
// then `LOCAL_PANIC_COUNT` in that thread is zero. This invariant holds before
243
245
// 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.
244
258
static GLOBAL_PANIC_COUNT : AtomicUsize = AtomicUsize :: new ( 0 ) ;
245
259
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
+ )
253
269
}
254
270
255
271
pub fn decrease ( ) {
@@ -261,13 +277,19 @@ pub mod panic_count {
261
277
} ) ;
262
278
}
263
279
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 {
265
286
LOCAL_PANIC_COUNT . with ( |c| c. get ( ) )
266
287
}
267
288
289
+ // Disregards ALWAYS_ABORT_FLAG
268
290
#[ 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 {
271
293
// Fast path: if `GLOBAL_PANIC_COUNT` is zero, all threads
272
294
// (including the current one) will have `LOCAL_PANIC_COUNT`
273
295
// 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>>
410
432
/// Determines whether the current thread is unwinding because of panic.
411
433
#[ inline]
412
434
pub fn panicking ( ) -> bool {
413
- !panic_count:: is_zero ( )
435
+ !panic_count:: count_is_zero ( )
414
436
}
415
437
416
438
/// The entry point for panicking with a formatted message.
@@ -563,15 +585,27 @@ fn rust_panic_with_hook(
563
585
message : Option < & fmt:: Arguments < ' _ > > ,
564
586
location : & Location < ' _ > ,
565
587
) -> ! {
566
- let panics = panic_count:: increase ( ) ;
588
+ let ( must_abort , panics) = panic_count:: increase ( ) ;
567
589
568
590
// If this is the third nested call (e.g., panics == 2, this is 0-indexed),
569
591
// the panic hook probably triggered the last panic, otherwise the
570
592
// double-panic check would have aborted the process. In this case abort the
571
593
// process real quickly as we don't want to try calling it again as it'll
572
594
// 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
+ }
575
609
intrinsics:: abort ( )
576
610
}
577
611
0 commit comments