@@ -89,12 +89,13 @@ mod tests;
8989// a backtrace or actually symbolizing it.
9090
9191use crate :: backtrace_rs:: { self , BytesOrWideString } ;
92+ use crate :: cell:: UnsafeCell ;
9293use crate :: env;
9394use crate :: ffi:: c_void;
9495use crate :: fmt;
9596use crate :: panic:: UnwindSafe ;
9697use crate :: sync:: atomic:: { AtomicUsize , Ordering :: Relaxed } ;
97- use crate :: sync:: LazyLock ;
98+ use crate :: sync:: Once ;
9899use crate :: sys_common:: backtrace:: { lock, output_filename} ;
99100use crate :: vec:: Vec ;
100101
@@ -133,14 +134,20 @@ pub enum BacktraceStatus {
133134enum Inner {
134135 Unsupported ,
135136 Disabled ,
136- Captured ( LazyLock < Capture , LazyResolve > ) ,
137+ Captured ( LazilyResolvedCapture ) ,
137138}
138139
139140struct Capture {
140141 actual_start : usize ,
142+ resolved : bool ,
141143 frames : Vec < BacktraceFrame > ,
142144}
143145
146+ fn _assert_send_sync ( ) {
147+ fn _assert < T : Send + Sync > ( ) { }
148+ _assert :: < Backtrace > ( ) ;
149+ }
150+
144151/// A single frame of a backtrace.
145152#[ unstable( feature = "backtrace_frames" , issue = "79676" ) ]
146153pub struct BacktraceFrame {
@@ -173,7 +180,7 @@ impl fmt::Debug for Backtrace {
173180 let capture = match & self . inner {
174181 Inner :: Unsupported => return fmt. write_str ( "<unsupported>" ) ,
175182 Inner :: Disabled => return fmt. write_str ( "<disabled>" ) ,
176- Inner :: Captured ( c) => & * * c ,
183+ Inner :: Captured ( c) => c . force ( ) ,
177184 } ;
178185
179186 let frames = & capture. frames [ capture. actual_start ..] ;
@@ -341,10 +348,11 @@ impl Backtrace {
341348 let inner = if frames. is_empty ( ) {
342349 Inner :: Unsupported
343350 } else {
344- Inner :: Captured ( LazyLock :: new ( lazy_resolve ( Capture {
351+ Inner :: Captured ( LazilyResolvedCapture :: new ( Capture {
345352 actual_start : actual_start. unwrap_or ( 0 ) ,
346353 frames,
347- } ) ) )
354+ resolved : false ,
355+ } ) )
348356 } ;
349357
350358 Backtrace { inner }
@@ -369,7 +377,7 @@ impl<'a> Backtrace {
369377 #[ must_use]
370378 #[ unstable( feature = "backtrace_frames" , issue = "79676" ) ]
371379 pub fn frames ( & ' a self ) -> & ' a [ BacktraceFrame ] {
372- if let Inner :: Captured ( c) = & self . inner { & c. frames } else { & [ ] }
380+ if let Inner :: Captured ( c) = & self . inner { & c. force ( ) . frames } else { & [ ] }
373381 }
374382}
375383
@@ -379,7 +387,7 @@ impl fmt::Display for Backtrace {
379387 let capture = match & self . inner {
380388 Inner :: Unsupported => return fmt. write_str ( "unsupported backtrace" ) ,
381389 Inner :: Disabled => return fmt. write_str ( "disabled backtrace" ) ,
382- Inner :: Captured ( c) => & * * c ,
390+ Inner :: Captured ( c) => c . force ( ) ,
383391 } ;
384392
385393 let full = fmt. alternate ( ) ;
@@ -423,15 +431,46 @@ impl fmt::Display for Backtrace {
423431 }
424432}
425433
426- type LazyResolve = impl FnOnce ( ) -> Capture + UnwindSafe ;
434+ struct LazilyResolvedCapture {
435+ sync : Once ,
436+ capture : UnsafeCell < Capture > ,
437+ }
438+
439+ impl LazilyResolvedCapture {
440+ fn new ( capture : Capture ) -> Self {
441+ LazilyResolvedCapture { sync : Once :: new ( ) , capture : UnsafeCell :: new ( capture) }
442+ }
443+
444+ fn force ( & self ) -> & Capture {
445+ self . sync . call_once ( || {
446+ // SAFETY: This exclusive reference can't overlap with any others
447+ // `Once` guarantees callers will block until this closure returns
448+ // `Once` also guarantees only a single caller will enter this closure
449+ unsafe { & mut * self . capture . get ( ) } . resolve ( ) ;
450+ } ) ;
451+
452+ // SAFETY: This shared reference can't overlap with the exclusive reference above
453+ unsafe { & * self . capture . get ( ) }
454+ }
455+ }
456+
457+ // SAFETY: Access to the inner value is synchronized using a thread-safe `Once`
458+ // So long as `Capture` is `Sync`, `LazilyResolvedCapture` is too
459+ unsafe impl Sync for LazilyResolvedCapture where Capture : Sync { }
460+
461+ impl Capture {
462+ fn resolve ( & mut self ) {
463+ // If we're already resolved, nothing to do!
464+ if self . resolved {
465+ return ;
466+ }
467+ self . resolved = true ;
427468
428- fn lazy_resolve ( mut capture : Capture ) -> LazyResolve {
429- move || {
430469 // Use the global backtrace lock to synchronize this as it's a
431470 // requirement of the `backtrace` crate, and then actually resolve
432471 // everything.
433472 let _lock = lock ( ) ;
434- for frame in capture . frames . iter_mut ( ) {
473+ for frame in self . frames . iter_mut ( ) {
435474 let symbols = & mut frame. symbols ;
436475 let frame = match & frame. frame {
437476 RawFrame :: Actual ( frame) => frame,
@@ -452,8 +491,6 @@ fn lazy_resolve(mut capture: Capture) -> LazyResolve {
452491 } ) ;
453492 }
454493 }
455-
456- capture
457494 }
458495}
459496
0 commit comments