@@ -170,7 +170,7 @@ pub fn take_hook() -> Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send> {
170170fn default_hook ( info : & PanicInfo < ' _ > ) {
171171 // If this is a double panic, make sure that we print a backtrace
172172 // 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 {
174174 RustBacktrace :: Print ( backtrace_rs:: PrintFmt :: Full )
175175 } else {
176176 backtrace:: rust_backtrace_env ( )
@@ -221,19 +221,65 @@ fn default_hook(info: &PanicInfo<'_>) {
221221#[ cfg( not( test) ) ]
222222#[ doc( hidden) ]
223223#[ unstable( feature = "update_panic_count" , issue = "none" ) ]
224- pub fn update_panic_count ( amt : isize ) -> usize {
224+ pub mod panic_count {
225225 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+ }
227255
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+ }
233279}
234280
235281#[ cfg( test) ]
236- pub use realstd:: rt:: update_panic_count ;
282+ pub use realstd:: rt:: panic_count ;
237283
238284/// Invoke a closure, capturing the cause of an unwinding panic if one occurs.
239285pub 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>>
283329 #[ cold]
284330 unsafe fn cleanup ( payload : * mut u8 ) -> Box < dyn Any + Send + ' static > {
285331 let obj = Box :: from_raw ( __rust_panic_cleanup ( payload) ) ;
286- update_panic_count ( - 1 ) ;
332+ panic_count :: decrease ( ) ;
287333 obj
288334 }
289335
@@ -312,8 +358,9 @@ pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>>
312358}
313359
314360/// Determines whether the current thread is unwinding because of panic.
361+ #[ inline]
315362pub fn panicking ( ) -> bool {
316- update_panic_count ( 0 ) != 0
363+ !panic_count :: is_zero ( )
317364}
318365
319366/// The entry point for panicking with a formatted message.
@@ -445,7 +492,7 @@ fn rust_panic_with_hook(
445492 message : Option < & fmt:: Arguments < ' _ > > ,
446493 location : & Location < ' _ > ,
447494) -> ! {
448- let panics = update_panic_count ( 1 ) ;
495+ let panics = panic_count :: increase ( ) ;
449496
450497 // If this is the third nested call (e.g., panics == 2, this is 0-indexed),
451498 // the panic hook probably triggered the last panic, otherwise the
@@ -495,7 +542,7 @@ fn rust_panic_with_hook(
495542/// This is the entry point for `resume_unwind`.
496543/// It just forwards the payload to the panic runtime.
497544pub fn rust_panic_without_hook ( payload : Box < dyn Any + Send > ) -> ! {
498- update_panic_count ( 1 ) ;
545+ panic_count :: increase ( ) ;
499546
500547 struct RewrapBox ( Box < dyn Any + Send > ) ;
501548
0 commit comments