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

Commit 6776af5

Browse files
committed
std: use LazyLock to lazily resolve backtraces
1 parent 8b8110e commit 6776af5

File tree

5 files changed

+35
-54
lines changed

5 files changed

+35
-54
lines changed

library/std/src/backtrace.rs

+13-50
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,20 +132,14 @@ 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

145-
fn _assert_send_sync() {
146-
fn _assert<T: Send + Sync>() {}
147-
_assert::<Backtrace>();
148-
}
149-
150143
/// A single frame of a backtrace.
151144
#[unstable(feature = "backtrace_frames", issue = "79676")]
152145
pub struct BacktraceFrame {
@@ -179,7 +172,7 @@ impl fmt::Debug for Backtrace {
179172
let capture = match &self.inner {
180173
Inner::Unsupported => return fmt.write_str("<unsupported>"),
181174
Inner::Disabled => return fmt.write_str("<disabled>"),
182-
Inner::Captured(c) => c.force(),
175+
Inner::Captured(c) => &**c,
183176
};
184177

185178
let frames = &capture.frames[capture.actual_start..];
@@ -347,11 +340,10 @@ impl Backtrace {
347340
let inner = if frames.is_empty() {
348341
Inner::Unsupported
349342
} else {
350-
Inner::Captured(LazilyResolvedCapture::new(Capture {
343+
Inner::Captured(LazyLock::new(lazy_resolve(Capture {
351344
actual_start: actual_start.unwrap_or(0),
352345
frames,
353-
resolved: false,
354-
}))
346+
})))
355347
};
356348

357349
Backtrace { inner }
@@ -376,7 +368,7 @@ impl<'a> Backtrace {
376368
#[must_use]
377369
#[unstable(feature = "backtrace_frames", issue = "79676")]
378370
pub fn frames(&'a self) -> &'a [BacktraceFrame] {
379-
if let Inner::Captured(c) = &self.inner { &c.force().frames } else { &[] }
371+
if let Inner::Captured(c) = &self.inner { &c.frames } else { &[] }
380372
}
381373
}
382374

@@ -386,7 +378,7 @@ impl fmt::Display for Backtrace {
386378
let capture = match &self.inner {
387379
Inner::Unsupported => return fmt.write_str("unsupported backtrace"),
388380
Inner::Disabled => return fmt.write_str("disabled backtrace"),
389-
Inner::Captured(c) => c.force(),
381+
Inner::Captured(c) => &**c,
390382
};
391383

392384
let full = fmt.alternate();
@@ -430,46 +422,15 @@ impl fmt::Display for Backtrace {
430422
}
431423
}
432424

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;
425+
type LazyResolve = impl FnOnce() -> Capture;
467426

427+
fn lazy_resolve(mut capture: Capture) -> LazyResolve {
428+
move || {
468429
// Use the global backtrace lock to synchronize this as it's a
469430
// requirement of the `backtrace` crate, and then actually resolve
470431
// everything.
471432
let _lock = lock();
472-
for frame in self.frames.iter_mut() {
433+
for frame in capture.frames.iter_mut() {
473434
let symbols = &mut frame.symbols;
474435
let frame = match &frame.frame {
475436
RawFrame::Actual(frame) => frame,
@@ -490,6 +451,8 @@ impl Capture {
490451
});
491452
}
492453
}
454+
455+
capture
493456
}
494457
}
495458

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
@@ -271,6 +271,7 @@
271271
#![feature(staged_api)]
272272
#![feature(thread_local)]
273273
#![feature(try_blocks)]
274+
#![feature(type_alias_impl_trait)]
274275
#![feature(utf8_chunks)]
275276
// tidy-alphabetical-end
276277
//

library/std/src/sync/lazy_lock.rs

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

72+
/// Creates a new lazy value that is already initialized.
73+
#[inline]
74+
#[cfg(test)]
75+
pub(crate) fn preinit(value: T) -> LazyLock<T, F> {
76+
let once = Once::new();
77+
once.call_once(|| {});
78+
LazyLock { once, data: UnsafeCell::new(Data { value: ManuallyDrop::new(value) }) }
79+
}
80+
7281
/// Consumes this `LazyLock` returning the stored value.
7382
///
7483
/// Returns `Ok(value)` if `Lazy` is initialized and `Err(f)` otherwise.

library/std/tests/backtrace.rs

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
use std::backtrace::Backtrace;
2+
3+
// Unfortunately, this cannot be a unit test because that causes problems
4+
// with type-alias-impl-trait (the assert counts as a defining use).
5+
#[test]
6+
fn assert_send_sync() {
7+
fn assert<T: Send + Sync>() {}
8+
9+
assert::<Backtrace>();
10+
}

0 commit comments

Comments
 (0)