Rollup merge of #109075 - joboet:lazylock_backtrace, r=workingjubilee

Use `LazyLock` to lazily resolve backtraces

By using TAIT to name the initializing closure, `LazyLock` can be used to replace the current `LazilyResolvedCapture`.
This commit is contained in:
Jubilee 2023-07-30 17:50:47 -07:00 committed by GitHub
commit 0ad8d6adc5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 25 additions and 49 deletions

View File

@ -89,12 +89,11 @@ mod tests;
// a backtrace or actually symbolizing it. // a backtrace or actually symbolizing it.
use crate::backtrace_rs::{self, BytesOrWideString}; use crate::backtrace_rs::{self, BytesOrWideString};
use crate::cell::UnsafeCell;
use crate::env; use crate::env;
use crate::ffi::c_void; use crate::ffi::c_void;
use crate::fmt; use crate::fmt;
use crate::sync::atomic::{AtomicUsize, Ordering::Relaxed}; use crate::sync::atomic::{AtomicUsize, Ordering::Relaxed};
use crate::sync::Once; use crate::sync::LazyLock;
use crate::sys_common::backtrace::{lock, output_filename}; use crate::sys_common::backtrace::{lock, output_filename};
use crate::vec::Vec; use crate::vec::Vec;
@ -133,12 +132,11 @@ pub enum BacktraceStatus {
enum Inner { enum Inner {
Unsupported, Unsupported,
Disabled, Disabled,
Captured(LazilyResolvedCapture), Captured(LazyLock<Capture, LazyResolve>),
} }
struct Capture { struct Capture {
actual_start: usize, actual_start: usize,
resolved: bool,
frames: Vec<BacktraceFrame>, frames: Vec<BacktraceFrame>,
} }
@ -179,7 +177,7 @@ impl fmt::Debug for Backtrace {
let capture = match &self.inner { let capture = match &self.inner {
Inner::Unsupported => return fmt.write_str("<unsupported>"), Inner::Unsupported => return fmt.write_str("<unsupported>"),
Inner::Disabled => return fmt.write_str("<disabled>"), Inner::Disabled => return fmt.write_str("<disabled>"),
Inner::Captured(c) => c.force(), Inner::Captured(c) => &**c,
}; };
let frames = &capture.frames[capture.actual_start..]; let frames = &capture.frames[capture.actual_start..];
@ -347,11 +345,10 @@ impl Backtrace {
let inner = if frames.is_empty() { let inner = if frames.is_empty() {
Inner::Unsupported Inner::Unsupported
} else { } else {
Inner::Captured(LazilyResolvedCapture::new(Capture { Inner::Captured(LazyLock::new(lazy_resolve(Capture {
actual_start: actual_start.unwrap_or(0), actual_start: actual_start.unwrap_or(0),
frames, frames,
resolved: false, })))
}))
}; };
Backtrace { inner } Backtrace { inner }
@ -376,7 +373,7 @@ impl<'a> Backtrace {
#[must_use] #[must_use]
#[unstable(feature = "backtrace_frames", issue = "79676")] #[unstable(feature = "backtrace_frames", issue = "79676")]
pub fn frames(&'a self) -> &'a [BacktraceFrame] { pub fn frames(&'a self) -> &'a [BacktraceFrame] {
if let Inner::Captured(c) = &self.inner { &c.force().frames } else { &[] } if let Inner::Captured(c) = &self.inner { &c.frames } else { &[] }
} }
} }
@ -386,7 +383,7 @@ impl fmt::Display for Backtrace {
let capture = match &self.inner { let capture = match &self.inner {
Inner::Unsupported => return fmt.write_str("unsupported backtrace"), Inner::Unsupported => return fmt.write_str("unsupported backtrace"),
Inner::Disabled => return fmt.write_str("disabled backtrace"), Inner::Disabled => return fmt.write_str("disabled backtrace"),
Inner::Captured(c) => c.force(), Inner::Captured(c) => &**c,
}; };
let full = fmt.alternate(); let full = fmt.alternate();
@ -430,46 +427,15 @@ impl fmt::Display for Backtrace {
} }
} }
struct LazilyResolvedCapture { type LazyResolve = impl (FnOnce() -> Capture) + Send + Sync;
sync: Once,
capture: UnsafeCell<Capture>,
}
impl LazilyResolvedCapture {
fn new(capture: Capture) -> Self {
LazilyResolvedCapture { sync: Once::new(), capture: UnsafeCell::new(capture) }
}
fn force(&self) -> &Capture {
self.sync.call_once(|| {
// SAFETY: This exclusive reference can't overlap with any others
// `Once` guarantees callers will block until this closure returns
// `Once` also guarantees only a single caller will enter this closure
unsafe { &mut *self.capture.get() }.resolve();
});
// SAFETY: This shared reference can't overlap with the exclusive reference above
unsafe { &*self.capture.get() }
}
}
// SAFETY: Access to the inner value is synchronized using a thread-safe `Once`
// So long as `Capture` is `Sync`, `LazilyResolvedCapture` is too
unsafe impl Sync for LazilyResolvedCapture where Capture: Sync {}
impl Capture {
fn resolve(&mut self) {
// If we're already resolved, nothing to do!
if self.resolved {
return;
}
self.resolved = true;
fn lazy_resolve(mut capture: Capture) -> LazyResolve {
move || {
// Use the global backtrace lock to synchronize this as it's a // Use the global backtrace lock to synchronize this as it's a
// requirement of the `backtrace` crate, and then actually resolve // requirement of the `backtrace` crate, and then actually resolve
// everything. // everything.
let _lock = lock(); let _lock = lock();
for frame in self.frames.iter_mut() { for frame in capture.frames.iter_mut() {
let symbols = &mut frame.symbols; let symbols = &mut frame.symbols;
let frame = match &frame.frame { let frame = match &frame.frame {
RawFrame::Actual(frame) => frame, RawFrame::Actual(frame) => frame,
@ -490,6 +456,8 @@ impl Capture {
}); });
} }
} }
capture
} }
} }

View File

@ -43,9 +43,8 @@ fn generate_fake_frames() -> Vec<BacktraceFrame> {
#[test] #[test]
fn test_debug() { fn test_debug() {
let backtrace = Backtrace { let backtrace = Backtrace {
inner: Inner::Captured(LazilyResolvedCapture::new(Capture { inner: Inner::Captured(LazyLock::preinit(Capture {
actual_start: 1, actual_start: 1,
resolved: true,
frames: generate_fake_frames(), frames: generate_fake_frames(),
})), })),
}; };
@ -66,9 +65,8 @@ fn test_debug() {
#[test] #[test]
fn test_frames() { fn test_frames() {
let backtrace = Backtrace { let backtrace = Backtrace {
inner: Inner::Captured(LazilyResolvedCapture::new(Capture { inner: Inner::Captured(LazyLock::preinit(Capture {
actual_start: 1, actual_start: 1,
resolved: true,
frames: generate_fake_frames(), frames: generate_fake_frames(),
})), })),
}; };

View File

@ -272,6 +272,7 @@
#![feature(staged_api)] #![feature(staged_api)]
#![feature(thread_local)] #![feature(thread_local)]
#![feature(try_blocks)] #![feature(try_blocks)]
#![feature(type_alias_impl_trait)]
#![feature(utf8_chunks)] #![feature(utf8_chunks)]
// tidy-alphabetical-end // tidy-alphabetical-end
// //

View File

@ -89,6 +89,15 @@ impl<T, F: FnOnce() -> T> LazyLock<T, F> {
LazyLock { once: Once::new(), data: UnsafeCell::new(Data { f: ManuallyDrop::new(f) }) } LazyLock { once: Once::new(), data: UnsafeCell::new(Data { f: ManuallyDrop::new(f) }) }
} }
/// Creates a new lazy value that is already initialized.
#[inline]
#[cfg(test)]
pub(crate) fn preinit(value: T) -> LazyLock<T, F> {
let once = Once::new();
once.call_once(|| {});
LazyLock { once, data: UnsafeCell::new(Data { value: ManuallyDrop::new(value) }) }
}
/// Consumes this `LazyLock` returning the stored value. /// Consumes this `LazyLock` returning the stored value.
/// ///
/// Returns `Ok(value)` if `Lazy` is initialized and `Err(f)` otherwise. /// Returns `Ok(value)` if `Lazy` is initialized and `Err(f)` otherwise.