Skip to content
/ rust Public
forked from rust-lang/rust

Commit 0ad8d6a

Browse files
Rollup merge of rust-lang#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`.
2 parents e5a6e5c + 29e3f8b commit 0ad8d6a

File tree

4 files changed

+25
-49
lines changed

4 files changed

+25
-49
lines changed

library/std/src/backtrace.rs

+13-45
Original file line numberDiff line numberDiff line change
@@ -89,12 +89,11 @@ mod tests;
8989
// a backtrace or actually symbolizing it.
9090

9191
use crate::backtrace_rs::{self, BytesOrWideString};
92-
use crate::cell::UnsafeCell;
9392
use crate::env;
9493
use crate::ffi::c_void;
9594
use crate::fmt;
9695
use crate::sync::atomic::{AtomicUsize, Ordering::Relaxed};
97-
use crate::sync::Once;
96+
use crate::sync::LazyLock;
9897
use crate::sys_common::backtrace::{lock, output_filename};
9998
use crate::vec::Vec;
10099

@@ -133,12 +132,11 @@ pub enum BacktraceStatus {
133132
enum Inner {
134133
Unsupported,
135134
Disabled,
136-
Captured(LazilyResolvedCapture),
135+
Captured(LazyLock<Capture, LazyResolve>),
137136
}
138137

139138
struct Capture {
140139
actual_start: usize,
141-
resolved: bool,
142140
frames: Vec<BacktraceFrame>,
143141
}
144142

@@ -179,7 +177,7 @@ impl fmt::Debug for Backtrace {
179177
let capture = match &self.inner {
180178
Inner::Unsupported => return fmt.write_str("<unsupported>"),
181179
Inner::Disabled => return fmt.write_str("<disabled>"),
182-
Inner::Captured(c) => c.force(),
180+
Inner::Captured(c) => &**c,
183181
};
184182

185183
let frames = &capture.frames[capture.actual_start..];
@@ -347,11 +345,10 @@ impl Backtrace {
347345
let inner = if frames.is_empty() {
348346
Inner::Unsupported
349347
} else {
350-
Inner::Captured(LazilyResolvedCapture::new(Capture {
348+
Inner::Captured(LazyLock::new(lazy_resolve(Capture {
351349
actual_start: actual_start.unwrap_or(0),
352350
frames,
353-
resolved: false,
354-
}))
351+
})))
355352
};
356353

357354
Backtrace { inner }
@@ -376,7 +373,7 @@ impl<'a> Backtrace {
376373
#[must_use]
377374
#[unstable(feature = "backtrace_frames", issue = "79676")]
378375
pub fn frames(&'a self) -> &'a [BacktraceFrame] {
379-
if let Inner::Captured(c) = &self.inner { &c.force().frames } else { &[] }
376+
if let Inner::Captured(c) = &self.inner { &c.frames } else { &[] }
380377
}
381378
}
382379

@@ -386,7 +383,7 @@ impl fmt::Display for Backtrace {
386383
let capture = match &self.inner {
387384
Inner::Unsupported => return fmt.write_str("unsupported backtrace"),
388385
Inner::Disabled => return fmt.write_str("disabled backtrace"),
389-
Inner::Captured(c) => c.force(),
386+
Inner::Captured(c) => &**c,
390387
};
391388

392389
let full = fmt.alternate();
@@ -430,46 +427,15 @@ impl fmt::Display for Backtrace {
430427
}
431428
}
432429

433-
struct LazilyResolvedCapture {
434-
sync: Once,
435-
capture: UnsafeCell<Capture>,
436-
}
437-
438-
impl LazilyResolvedCapture {
439-
fn new(capture: Capture) -> Self {
440-
LazilyResolvedCapture { sync: Once::new(), capture: UnsafeCell::new(capture) }
441-
}
442-
443-
fn force(&self) -> &Capture {
444-
self.sync.call_once(|| {
445-
// SAFETY: This exclusive reference can't overlap with any others
446-
// `Once` guarantees callers will block until this closure returns
447-
// `Once` also guarantees only a single caller will enter this closure
448-
unsafe { &mut *self.capture.get() }.resolve();
449-
});
450-
451-
// SAFETY: This shared reference can't overlap with the exclusive reference above
452-
unsafe { &*self.capture.get() }
453-
}
454-
}
455-
456-
// SAFETY: Access to the inner value is synchronized using a thread-safe `Once`
457-
// So long as `Capture` is `Sync`, `LazilyResolvedCapture` is too
458-
unsafe impl Sync for LazilyResolvedCapture where Capture: Sync {}
459-
460-
impl Capture {
461-
fn resolve(&mut self) {
462-
// If we're already resolved, nothing to do!
463-
if self.resolved {
464-
return;
465-
}
466-
self.resolved = true;
430+
type LazyResolve = impl (FnOnce() -> Capture) + Send + Sync;
467431

432+
fn lazy_resolve(mut capture: Capture) -> LazyResolve {
433+
move || {
468434
// Use the global backtrace lock to synchronize this as it's a
469435
// requirement of the `backtrace` crate, and then actually resolve
470436
// everything.
471437
let _lock = lock();
472-
for frame in self.frames.iter_mut() {
438+
for frame in capture.frames.iter_mut() {
473439
let symbols = &mut frame.symbols;
474440
let frame = match &frame.frame {
475441
RawFrame::Actual(frame) => frame,
@@ -490,6 +456,8 @@ impl Capture {
490456
});
491457
}
492458
}
459+
460+
capture
493461
}
494462
}
495463

library/std/src/backtrace/tests.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,8 @@ fn generate_fake_frames() -> Vec<BacktraceFrame> {
4343
#[test]
4444
fn test_debug() {
4545
let backtrace = Backtrace {
46-
inner: Inner::Captured(LazilyResolvedCapture::new(Capture {
46+
inner: Inner::Captured(LazyLock::preinit(Capture {
4747
actual_start: 1,
48-
resolved: true,
4948
frames: generate_fake_frames(),
5049
})),
5150
};
@@ -66,9 +65,8 @@ fn test_debug() {
6665
#[test]
6766
fn test_frames() {
6867
let backtrace = Backtrace {
69-
inner: Inner::Captured(LazilyResolvedCapture::new(Capture {
68+
inner: Inner::Captured(LazyLock::preinit(Capture {
7069
actual_start: 1,
71-
resolved: true,
7270
frames: generate_fake_frames(),
7371
})),
7472
};

library/std/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@
272272
#![feature(staged_api)]
273273
#![feature(thread_local)]
274274
#![feature(try_blocks)]
275+
#![feature(type_alias_impl_trait)]
275276
#![feature(utf8_chunks)]
276277
// tidy-alphabetical-end
277278
//

library/std/src/sync/lazy_lock.rs

+9
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,15 @@ impl<T, F: FnOnce() -> T> LazyLock<T, F> {
8989
LazyLock { once: Once::new(), data: UnsafeCell::new(Data { f: ManuallyDrop::new(f) }) }
9090
}
9191

92+
/// Creates a new lazy value that is already initialized.
93+
#[inline]
94+
#[cfg(test)]
95+
pub(crate) fn preinit(value: T) -> LazyLock<T, F> {
96+
let once = Once::new();
97+
once.call_once(|| {});
98+
LazyLock { once, data: UnsafeCell::new(Data { value: ManuallyDrop::new(value) }) }
99+
}
100+
92101
/// Consumes this `LazyLock` returning the stored value.
93102
///
94103
/// Returns `Ok(value)` if `Lazy` is initialized and `Err(f)` otherwise.

0 commit comments

Comments
 (0)