Skip to content

Commit 5158b3c

Browse files
authored
Rollup merge of #72617 - eduardosm:panicking, r=Amanieu
Add a fast path for `std::thread::panicking`. This is done by adding a global atomic variable (non-TLS) that counts how many threads are panicking. In order to check if the current thread is panicking, this variable is read and, if it is zero, no thread (including the one where `panicking` is being called) is panicking and `panicking` can return `false` immediately without needing to access TLS. If the global counter is not zero, the local counter is accessed from TLS to check if the current thread is panicking.
2 parents 50fc24d + 771a1d8 commit 5158b3c

File tree

2 files changed

+61
-14
lines changed

2 files changed

+61
-14
lines changed

src/libstd/panicking.rs

+60-13
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ pub fn take_hook() -> Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send> {
170170
fn 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.
239285
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>>
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]
315362
pub 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.
497544
pub 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

src/libstd/rt.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
#![doc(hidden)]
1616

1717
// Re-export some of our utilities which are expected by other crates.
18-
pub use crate::panicking::{begin_panic, begin_panic_fmt, update_panic_count};
18+
pub use crate::panicking::{begin_panic, begin_panic_fmt, panic_count};
1919

2020
// To reduce the generated code of the new `lang_start`, this function is doing
2121
// the real work.

0 commit comments

Comments
 (0)