@@ -150,11 +150,10 @@ use core::{
150150use std:: {
151151 cell:: { Cell , RefCell , RefMut } ,
152152 error,
153- sync:: Weak ,
154153} ;
155154
156155#[ cfg( feature = "alloc" ) ]
157- use alloc:: sync:: Arc ;
156+ use alloc:: sync:: { Arc , Weak } ;
158157
159158#[ cfg( feature = "alloc" ) ]
160159use core:: ops:: Deref ;
@@ -169,6 +168,34 @@ pub struct Dispatch {
169168 collector : & ' static ( dyn Collect + Send + Sync ) ,
170169}
171170
171+ /// `WeakDispatch` is a version of [`Dispatch`] that holds a non-owning reference
172+ /// to a [collector].
173+ ///
174+ /// The collector may be accessed by calling [`WeakDispatch::upgrade`],
175+ /// which returns an `Option<Dispatch>`. If all [`Dispatch`] clones that point
176+ /// at the collector have been dropped, [`WeakDispatch::upgrade`] will return
177+ /// `None`. Otherwise, it will return `Some(Dispatch)`.
178+ ///
179+ /// A `WeakDispatch` may be created from a [`Dispatch`] by calling the
180+ /// [`Dispatch::downgrade`] method. The primary use for creating a
181+ /// [`WeakDispatch`] is to allow a collector to hold a cyclical reference to
182+ /// itself without creating a memory leak. See [here] for details.
183+ ///
184+ /// This type is analogous to the [`std::sync::Weak`] type, but for a
185+ /// [`Dispatch`] rather than an [`Arc`].
186+ ///
187+ /// [collector]: Collect
188+ /// [`Arc`]: std::sync::Arc
189+ /// [here]: Collect#avoiding-memory-leaks
190+ #[ derive( Clone ) ]
191+ pub struct WeakDispatch {
192+ #[ cfg( feature = "alloc" ) ]
193+ collector : Kind < Weak < dyn Collect + Send + Sync > > ,
194+
195+ #[ cfg( not( feature = "alloc" ) ) ]
196+ collector : & ' static ( dyn Collect + Send + Sync ) ,
197+ }
198+
172199#[ cfg( feature = "alloc" ) ]
173200#[ derive( Clone ) ]
174201enum Kind < T > {
@@ -577,6 +604,33 @@ impl Dispatch {
577604 me
578605 }
579606
607+ /// Creates a [`WeakDispatch`] from this `Dispatch`.
608+ ///
609+ /// A [`WeakDispatch`] is similar to a [`Dispatch`], but it does not prevent
610+ /// the underlying [collector] from being dropped. Instead, it only permits
611+ /// access while other references to the collector exist. This is equivalent
612+ /// to the standard library's [`Arc::downgrade`] method, but for `Dispatch`
613+ /// rather than `Arc`.
614+ ///
615+ /// The primary use for creating a [`WeakDispatch`] is to allow a collector
616+ /// to hold a cyclical reference to itself without creating a memory leak.
617+ /// See [here] for details.
618+ ///
619+ /// [collector]: Collect
620+ /// [`Arc::downgrade`]: std::sync::Arc::downgrade
621+ /// [here]: Collect#avoiding-memory-leaks
622+ pub fn downgrade ( & self ) -> WeakDispatch {
623+ #[ cfg( feature = "alloc" ) ]
624+ let collector = match & self . collector {
625+ Kind :: Global ( dispatch) => Kind :: Global ( * dispatch) ,
626+ Kind :: Scoped ( dispatch) => Kind :: Scoped ( Arc :: downgrade ( dispatch) ) ,
627+ } ;
628+ #[ cfg( not( feature = "alloc" ) ) ]
629+ let collector = self . collector ;
630+
631+ WeakDispatch { collector }
632+ }
633+
580634 #[ cfg( feature = "std" ) ]
581635 pub ( crate ) fn registrar ( & self ) -> Registrar {
582636 Registrar ( match self . collector {
@@ -859,6 +913,63 @@ where
859913 }
860914}
861915
916+ impl WeakDispatch {
917+ /// Attempts to upgrade this `WeakDispatch` to a [`Dispatch`].
918+ ///
919+ /// Returns `None` if the referenced `Dispatch` has already been dropped.
920+ ///
921+ /// ## Examples
922+ ///
923+ /// ```
924+ /// # use tracing_core::collect::NoCollector;
925+ /// # use tracing_core::dispatch::Dispatch;
926+ /// static COLLECTOR: NoCollector = NoCollector::new();
927+ /// let strong = Dispatch::new(COLLECTOR);
928+ /// let weak = strong.downgrade();
929+ ///
930+ /// // The strong here keeps it alive, so we can still access the object.
931+ /// assert!(weak.upgrade().is_some());
932+ ///
933+ /// drop(strong); // But not any more.
934+ /// assert!(weak.upgrade().is_none());
935+ /// ```
936+ pub fn upgrade ( & self ) -> Option < Dispatch > {
937+ #[ cfg( feature = "alloc" ) ]
938+ let collector = match & self . collector {
939+ Kind :: Global ( dispatch) => Some ( Kind :: Global ( * dispatch) ) ,
940+ Kind :: Scoped ( dispatch) => dispatch. upgrade ( ) . map ( Kind :: Scoped ) ,
941+ } ;
942+ #[ cfg( not( feature = "alloc" ) ) ]
943+ let collector = Some ( self . collector ) ;
944+
945+ collector. map ( |collector| Dispatch { collector } )
946+ }
947+ }
948+
949+ impl fmt:: Debug for WeakDispatch {
950+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
951+ match & self . collector {
952+ #[ cfg( feature = "alloc" ) ]
953+ Kind :: Global ( collector) => f
954+ . debug_tuple ( "WeakDispatch::Global" )
955+ . field ( & format_args ! ( "{:p}" , collector) )
956+ . finish ( ) ,
957+
958+ #[ cfg( feature = "alloc" ) ]
959+ Kind :: Scoped ( collector) => f
960+ . debug_tuple ( "WeakDispatch::Scoped" )
961+ . field ( & format_args ! ( "{:p}" , collector) )
962+ . finish ( ) ,
963+
964+ #[ cfg( not( feature = "alloc" ) ) ]
965+ collector => f
966+ . debug_tuple ( "WeakDispatch::Global" )
967+ . field ( & format_args ! ( "{:p}" , collector) )
968+ . finish ( ) ,
969+ }
970+ }
971+ }
972+
862973#[ cfg( feature = "std" ) ]
863974impl Registrar {
864975 pub ( crate ) fn upgrade ( & self ) -> Option < Dispatch > {
0 commit comments