@@ -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 ( )
@@ -222,19 +222,56 @@ fn default_hook(info: &PanicInfo<'_>) {
222222#[ cfg( not( test) ) ]
223223#[ doc( hidden) ]
224224#[ unstable( feature = "update_panic_count" , issue = "none" ) ]
225- pub fn update_panic_count ( amt : isize ) -> usize {
225+ pub mod panic_count {
226226 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+ }
228256
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+ }
234271}
235272
236273#[ cfg( test) ]
237- pub use realstd:: rt:: update_panic_count ;
274+ pub use realstd:: rt:: panic_count ;
238275
239276/// Invoke a closure, capturing the cause of an unwinding panic if one occurs.
240277pub 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>>
284321 #[ cold]
285322 unsafe fn cleanup ( payload : * mut u8 ) -> Box < dyn Any + Send + ' static > {
286323 let obj = Box :: from_raw ( __rust_panic_cleanup ( payload) ) ;
287- update_panic_count ( - 1 ) ;
324+ panic_count :: decrease ( ) ;
288325 obj
289326 }
290327
@@ -314,7 +351,7 @@ pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>>
314351
315352/// Determines whether the current thread is unwinding because of panic.
316353pub fn panicking ( ) -> bool {
317- update_panic_count ( 0 ) != 0
354+ !panic_count :: is_zero ( )
318355}
319356
320357/// The entry point for panicking with a formatted message.
@@ -452,7 +489,7 @@ fn rust_panic_with_hook(
452489 message : Option < & fmt:: Arguments < ' _ > > ,
453490 location : & Location < ' _ > ,
454491) -> ! {
455- let panics = update_panic_count ( 1 ) ;
492+ let panics = panic_count :: increase ( ) ;
456493
457494 // If this is the third nested call (e.g., panics == 2, this is 0-indexed),
458495 // the panic hook probably triggered the last panic, otherwise the
@@ -514,7 +551,7 @@ fn rust_panic_with_hook(
514551/// This is the entry point for `resume_unwind`.
515552/// It just forwards the payload to the panic runtime.
516553pub fn rust_panic_without_hook ( payload : Box < dyn Any + Send > ) -> ! {
517- update_panic_count ( 1 ) ;
554+ panic_count :: increase ( ) ;
518555
519556 struct RewrapBox ( Box < dyn Any + Send > ) ;
520557
0 commit comments