@@ -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 ( )
@@ -221,19 +221,65 @@ fn default_hook(info: &PanicInfo<'_>) {
221
221
#[ cfg( not( test) ) ]
222
222
#[ doc( hidden) ]
223
223
#[ unstable( feature = "update_panic_count" , issue = "none" ) ]
224
- pub fn update_panic_count ( amt : isize ) -> usize {
224
+ pub mod panic_count {
225
225
use crate :: cell:: Cell ;
226
- thread_local ! { static PANIC_COUNT : Cell <usize > = Cell :: new( 0 ) }
226
+ use crate :: sync:: atomic:: { AtomicUsize , Ordering } ;
227
+
228
+ // Panic count for the current thread.
229
+ thread_local ! { static LOCAL_PANIC_COUNT : Cell <usize > = Cell :: new( 0 ) }
230
+
231
+ // Sum of panic counts from all threads. The purpose of this is to have
232
+ // a fast path in `is_zero` (which is used by `panicking`). Access to
233
+ // this variable can be always be done with relaxed ordering because
234
+ // it is always guaranteed that, if `GLOBAL_PANIC_COUNT` is zero,
235
+ // `LOCAL_PANIC_COUNT` will be zero.
236
+ static GLOBAL_PANIC_COUNT : AtomicUsize = AtomicUsize :: new ( 0 ) ;
237
+
238
+ pub fn increase ( ) -> usize {
239
+ GLOBAL_PANIC_COUNT . fetch_add ( 1 , Ordering :: Relaxed ) ;
240
+ LOCAL_PANIC_COUNT . with ( |c| {
241
+ let next = c. get ( ) + 1 ;
242
+ c. set ( next) ;
243
+ next
244
+ } )
245
+ }
246
+
247
+ pub fn decrease ( ) -> usize {
248
+ GLOBAL_PANIC_COUNT . fetch_sub ( 1 , Ordering :: Relaxed ) ;
249
+ LOCAL_PANIC_COUNT . with ( |c| {
250
+ let next = c. get ( ) - 1 ;
251
+ c. set ( next) ;
252
+ next
253
+ } )
254
+ }
227
255
228
- PANIC_COUNT . with ( |c| {
229
- let next = ( c. get ( ) as isize + amt) as usize ;
230
- c. set ( next) ;
231
- next
232
- } )
256
+ pub fn get ( ) -> usize {
257
+ LOCAL_PANIC_COUNT . with ( |c| c. get ( ) )
258
+ }
259
+
260
+ #[ inline]
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
+ is_zero_slow_path ( )
269
+ }
270
+ }
271
+
272
+ // Slow path is in a separate function to reduce the amount of code
273
+ // inlined from `is_zero`.
274
+ #[ inline( never) ]
275
+ #[ cold]
276
+ fn is_zero_slow_path ( ) -> bool {
277
+ LOCAL_PANIC_COUNT . with ( |c| c. get ( ) == 0 )
278
+ }
233
279
}
234
280
235
281
#[ cfg( test) ]
236
- pub use realstd:: rt:: update_panic_count ;
282
+ pub use realstd:: rt:: panic_count ;
237
283
238
284
/// Invoke a closure, capturing the cause of an unwinding panic if one occurs.
239
285
pub unsafe fn r#try < R , F : FnOnce ( ) -> R > ( f : F ) -> Result < R , Box < dyn Any + Send > > {
@@ -283,7 +329,7 @@ pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>>
283
329
#[ cold]
284
330
unsafe fn cleanup ( payload : * mut u8 ) -> Box < dyn Any + Send + ' static > {
285
331
let obj = Box :: from_raw ( __rust_panic_cleanup ( payload) ) ;
286
- update_panic_count ( - 1 ) ;
332
+ panic_count :: decrease ( ) ;
287
333
obj
288
334
}
289
335
@@ -312,8 +358,9 @@ pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>>
312
358
}
313
359
314
360
/// Determines whether the current thread is unwinding because of panic.
361
+ #[ inline]
315
362
pub fn panicking ( ) -> bool {
316
- update_panic_count ( 0 ) != 0
363
+ !panic_count :: is_zero ( )
317
364
}
318
365
319
366
/// The entry point for panicking with a formatted message.
@@ -445,7 +492,7 @@ fn rust_panic_with_hook(
445
492
message : Option < & fmt:: Arguments < ' _ > > ,
446
493
location : & Location < ' _ > ,
447
494
) -> ! {
448
- let panics = update_panic_count ( 1 ) ;
495
+ let panics = panic_count :: increase ( ) ;
449
496
450
497
// If this is the third nested call (e.g., panics == 2, this is 0-indexed),
451
498
// the panic hook probably triggered the last panic, otherwise the
@@ -495,7 +542,7 @@ fn rust_panic_with_hook(
495
542
/// This is the entry point for `resume_unwind`.
496
543
/// It just forwards the payload to the panic runtime.
497
544
pub fn rust_panic_without_hook ( payload : Box < dyn Any + Send > ) -> ! {
498
- update_panic_count ( 1 ) ;
545
+ panic_count :: increase ( ) ;
499
546
500
547
struct RewrapBox ( Box < dyn Any + Send > ) ;
501
548
0 commit comments