@@ -170,7 +170,7 @@ pub fn take_hook() -> Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send> {
170
170
fn default_hook ( info : & PanicInfo < ' _ > ) {
171
171
// If this is a double panic, make sure that we print a backtrace
172
172
// for this panic. Otherwise only print it if logging is enabled.
173
- let backtrace_env = if update_panic_count ( 0 ) >= 2 {
173
+ let backtrace_env = if panic_count :: get ( ) >= 2 {
174
174
RustBacktrace :: Print ( backtrace_rs:: PrintFmt :: Full )
175
175
} else {
176
176
backtrace:: rust_backtrace_env ( )
@@ -222,19 +222,56 @@ fn default_hook(info: &PanicInfo<'_>) {
222
222
#[ cfg( not( test) ) ]
223
223
#[ doc( hidden) ]
224
224
#[ unstable( feature = "update_panic_count" , issue = "none" ) ]
225
- pub fn update_panic_count ( amt : isize ) -> usize {
225
+ pub mod panic_count {
226
226
use crate :: cell:: Cell ;
227
- thread_local ! { static PANIC_COUNT : Cell <usize > = Cell :: new( 0 ) }
227
+ use crate :: sync:: atomic:: { AtomicUsize , Ordering } ;
228
+
229
+ // Panic count for the current thread.
230
+ thread_local ! { static LOCAL_PANIC_COUNT : Cell <usize > = Cell :: new( 0 ) }
231
+
232
+ // Sum of panic counts from all threads. The purpose of this is to have
233
+ // a fast path in `is_zero` (which is used by `panicking`). Access to
234
+ // this variable can be always be done with relaxed ordering because
235
+ // it is always guaranteed that, if `GLOBAL_PANIC_COUNT` is zero,
236
+ // `LOCAL_PANIC_COUNT` will be zero.
237
+ static GLOBAL_PANIC_COUNT : AtomicUsize = AtomicUsize :: new ( 0 ) ;
238
+
239
+ pub fn increase ( ) -> usize {
240
+ GLOBAL_PANIC_COUNT . fetch_add ( 1 , Ordering :: Relaxed ) ;
241
+ LOCAL_PANIC_COUNT . with ( |c| {
242
+ let next = c. get ( ) + 1 ;
243
+ c. set ( next) ;
244
+ next
245
+ } )
246
+ }
247
+
248
+ pub fn decrease ( ) -> usize {
249
+ GLOBAL_PANIC_COUNT . fetch_sub ( 1 , Ordering :: Relaxed ) ;
250
+ LOCAL_PANIC_COUNT . with ( |c| {
251
+ let next = c. get ( ) - 1 ;
252
+ c. set ( next) ;
253
+ next
254
+ } )
255
+ }
228
256
229
- PANIC_COUNT . with ( |c| {
230
- let next = ( c. get ( ) as isize + amt) as usize ;
231
- c. set ( next) ;
232
- next
233
- } )
257
+ pub fn get ( ) -> usize {
258
+ LOCAL_PANIC_COUNT . with ( |c| c. get ( ) )
259
+ }
260
+
261
+ pub fn is_zero ( ) -> bool {
262
+ if GLOBAL_PANIC_COUNT . load ( Ordering :: Relaxed ) == 0 {
263
+ // Fast path: if `GLOBAL_PANIC_COUNT` is zero, all threads
264
+ // (including the current one) will have `LOCAL_PANIC_COUNT`
265
+ // equal to zero, so TLS access can be avoided.
266
+ true
267
+ } else {
268
+ LOCAL_PANIC_COUNT . with ( |c| c. get ( ) == 0 )
269
+ }
270
+ }
234
271
}
235
272
236
273
#[ cfg( test) ]
237
- pub use realstd:: rt:: update_panic_count ;
274
+ pub use realstd:: rt:: panic_count ;
238
275
239
276
/// Invoke a closure, capturing the cause of an unwinding panic if one occurs.
240
277
pub unsafe fn r#try < R , F : FnOnce ( ) -> R > ( f : F ) -> Result < R , Box < dyn Any + Send > > {
@@ -284,7 +321,7 @@ pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>>
284
321
#[ cold]
285
322
unsafe fn cleanup ( payload : * mut u8 ) -> Box < dyn Any + Send + ' static > {
286
323
let obj = Box :: from_raw ( __rust_panic_cleanup ( payload) ) ;
287
- update_panic_count ( - 1 ) ;
324
+ panic_count :: decrease ( ) ;
288
325
obj
289
326
}
290
327
@@ -314,7 +351,7 @@ pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>>
314
351
315
352
/// Determines whether the current thread is unwinding because of panic.
316
353
pub fn panicking ( ) -> bool {
317
- update_panic_count ( 0 ) != 0
354
+ !panic_count :: is_zero ( )
318
355
}
319
356
320
357
/// The entry point for panicking with a formatted message.
@@ -452,7 +489,7 @@ fn rust_panic_with_hook(
452
489
message : Option < & fmt:: Arguments < ' _ > > ,
453
490
location : & Location < ' _ > ,
454
491
) -> ! {
455
- let panics = update_panic_count ( 1 ) ;
492
+ let panics = panic_count :: increase ( ) ;
456
493
457
494
// If this is the third nested call (e.g., panics == 2, this is 0-indexed),
458
495
// the panic hook probably triggered the last panic, otherwise the
@@ -514,7 +551,7 @@ fn rust_panic_with_hook(
514
551
/// This is the entry point for `resume_unwind`.
515
552
/// It just forwards the payload to the panic runtime.
516
553
pub fn rust_panic_without_hook ( payload : Box < dyn Any + Send > ) -> ! {
517
- update_panic_count ( 1 ) ;
554
+ panic_count :: increase ( ) ;
518
555
519
556
struct RewrapBox ( Box < dyn Any + Send > ) ;
520
557
0 commit comments