• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Callsites represent the source locations from which spans or events
2 //! originate.
3 //!
4 //! # What Are Callsites?
5 //!
6 //! Every span or event in `tracing` is associated with a [`Callsite`]. A
7 //! callsite is a small `static` value that is responsible for the following:
8 //!
9 //! * Storing the span or event's [`Metadata`],
10 //! * Uniquely [identifying](Identifier) the span or event definition,
11 //! * Caching the subscriber's [`Interest`][^1] in that span or event, to avoid
12 //!   re-evaluating filters,
13 //! * Storing a [`Registration`] that allows the callsite to be part of a global
14 //!   list of all callsites in the program.
15 //!
16 //! # Registering Callsites
17 //!
18 //! When a span or event is recorded for the first time, its callsite
19 //! [`register`]s itself with the global callsite registry. Registering a
20 //! callsite calls the [`Subscriber::register_callsite`][`register_callsite`]
21 //! method with that callsite's [`Metadata`] on every currently active
22 //! subscriber. This serves two primary purposes: informing subscribers of the
23 //! callsite's existence, and performing static filtering.
24 //!
25 //! ## Callsite Existence
26 //!
27 //! If a [`Subscriber`] implementation wishes to allocate storage for each
28 //! unique span/event location in the program, or pre-compute some value
29 //! that will be used to record that span or event in the future, it can
30 //! do so in its [`register_callsite`] method.
31 //!
32 //! ## Performing Static Filtering
33 //!
34 //! The [`register_callsite`] method returns an [`Interest`] value,
35 //! which indicates that the subscriber either [always] wishes to record
36 //! that span or event, [sometimes] wishes to record it based on a
37 //! dynamic filter evaluation, or [never] wishes to record it.
38 //!
39 //! When registering a new callsite, the [`Interest`]s returned by every
40 //! currently active subscriber are combined, and the result is stored at
41 //! each callsite. This way, when the span or event occurs in the
42 //! future, the cached [`Interest`] value can be checked efficiently
43 //! to determine if the span or event should be recorded, without
44 //! needing to perform expensive filtering (i.e. calling the
45 //! [`Subscriber::enabled`] method every time a span or event occurs).
46 //!
47 //! ### Rebuilding Cached Interest
48 //!
49 //! When a new [`Dispatch`] is created (i.e. a new subscriber becomes
50 //! active), any previously cached [`Interest`] values are re-evaluated
51 //! for all callsites in the program. This way, if the new subscriber
52 //! will enable a callsite that was not previously enabled, the
53 //! [`Interest`] in that callsite is updated. Similarly, when a
54 //! subscriber is dropped, the interest cache is also re-evaluated, so
55 //! that any callsites enabled only by that subscriber are disabled.
56 //!
57 //! In addition, the [`rebuild_interest_cache`] function in this module can be
58 //! used to manually invalidate all cached interest and re-register those
59 //! callsites. This function is useful in situations where a subscriber's
60 //! interest can change, but it does so relatively infrequently. The subscriber
61 //! may wish for its interest to be cached most of the time, and return
62 //! [`Interest::always`][always] or [`Interest::never`][never] in its
63 //! [`register_callsite`] method, so that its [`Subscriber::enabled`] method
64 //! doesn't need to be evaluated every time a span or event is recorded.
65 //! However, when the configuration changes, the subscriber can call
66 //! [`rebuild_interest_cache`] to re-evaluate the entire interest cache with its
67 //! new configuration. This is a relatively costly operation, but if the
68 //! configuration changes infrequently, it may be more efficient than calling
69 //! [`Subscriber::enabled`] frequently.
70 //!
71 //! # Implementing Callsites
72 //!
73 //! In most cases, instrumenting code using `tracing` should *not* require
74 //! implementing the [`Callsite`] trait directly. When using the [`tracing`
75 //! crate's macros][macros] or the [`#[instrument]` attribute][instrument], a
76 //! `Callsite` is automatically generated.
77 //!
78 //! However, code which provides alternative forms of `tracing` instrumentation
79 //! may need to interact with the callsite system directly. If
80 //! instrumentation-side code needs to produce a `Callsite` to emit spans or
81 //! events, the [`DefaultCallsite`] struct provided in this module is a
82 //! ready-made `Callsite` implementation that is suitable for most uses. When
83 //! possible, the use of `DefaultCallsite` should be preferred over implementing
84 //! [`Callsite`] for user types, as `DefaultCallsite` may benefit from
85 //! additional performance optimizations.
86 //!
87 //! [^1]: Returned by the [`Subscriber::register_callsite`][`register_callsite`]
88 //!     method.
89 //!
90 //! [`Metadata`]: crate::metadata::Metadata
91 //! [`Interest`]: crate::subscriber::Interest
92 //! [`Subscriber`]: crate::subscriber::Subscriber
93 //! [`register_callsite`]: crate::subscriber::Subscriber::register_callsite
94 //! [`Subscriber::enabled`]: crate::subscriber::Subscriber::enabled
95 //! [always]: crate::subscriber::Interest::always
96 //! [sometimes]: crate::subscriber::Interest::sometimes
97 //! [never]: crate::subscriber::Interest::never
98 //! [`Dispatch`]: crate::dispatch::Dispatch
99 //! [macros]: https://docs.rs/tracing/latest/tracing/#macros
100 //! [instrument]: https://docs.rs/tracing/latest/tracing/attr.instrument.html
101 use crate::stdlib::{
102     any::TypeId,
103     fmt,
104     hash::{Hash, Hasher},
105     ptr,
106     sync::{
107         atomic::{AtomicBool, AtomicPtr, AtomicU8, Ordering},
108         Mutex,
109     },
110     vec::Vec,
111 };
112 use crate::{
113     dispatcher::Dispatch,
114     lazy::Lazy,
115     metadata::{LevelFilter, Metadata},
116     subscriber::Interest,
117 };
118 
119 use self::dispatchers::Dispatchers;
120 
121 /// Trait implemented by callsites.
122 ///
123 /// These functions are only intended to be called by the callsite registry, which
124 /// correctly handles determining the common interest between all subscribers.
125 ///
126 /// See the [module-level documentation](crate::callsite) for details on
127 /// callsites.
128 pub trait Callsite: Sync {
129     /// Sets the [`Interest`] for this callsite.
130     ///
131     /// See the [documentation on callsite interest caching][cache-docs] for
132     /// details.
133     ///
134     /// [`Interest`]: super::subscriber::Interest
135     /// [cache-docs]: crate::callsite#performing-static-filtering
set_interest(&self, interest: Interest)136     fn set_interest(&self, interest: Interest);
137 
138     /// Returns the [metadata] associated with the callsite.
139     ///
140     /// <div class="example-wrap" style="display:inline-block">
141     /// <pre class="ignore" style="white-space:normal;font:inherit;">
142     ///
143     /// **Note:** Implementations of this method should not produce [`Metadata`]
144     /// that share the same callsite [`Identifier`] but otherwise differ in any
145     /// way (e.g., have different `name`s).
146     ///
147     /// </pre></div>
148     ///
149     /// [metadata]: super::metadata::Metadata
metadata(&self) -> &Metadata<'_>150     fn metadata(&self) -> &Metadata<'_>;
151 
152     /// This method is an *internal implementation detail* of `tracing-core`. It
153     /// is *not* intended to be called or overridden from downstream code.
154     ///
155     /// The `Private` type can only be constructed from within `tracing-core`.
156     /// Because this method takes a `Private` as an argument, it cannot be
157     /// called from (safe) code external to `tracing-core`. Because it must
158     /// *return* a `Private`, the only valid implementation possible outside of
159     /// `tracing-core` would have to always unconditionally panic.
160     ///
161     /// THIS IS BY DESIGN. There is currently no valid reason for code outside
162     /// of `tracing-core` to override this method.
163     // TODO(eliza): this could be used to implement a public downcasting API
164     // for `&dyn Callsite`s in the future.
165     #[doc(hidden)]
166     #[inline]
private_type_id(&self, _: private::Private<()>) -> private::Private<TypeId> where Self: 'static,167     fn private_type_id(&self, _: private::Private<()>) -> private::Private<TypeId>
168     where
169         Self: 'static,
170     {
171         private::Private(TypeId::of::<Self>())
172     }
173 }
174 
175 /// Uniquely identifies a [`Callsite`]
176 ///
177 /// Two `Identifier`s are equal if they both refer to the same callsite.
178 ///
179 /// [`Callsite`]: super::callsite::Callsite
180 #[derive(Clone)]
181 pub struct Identifier(
182     /// **Warning**: The fields on this type are currently `pub` because it must
183     /// be able to be constructed statically by macros. However, when `const
184     /// fn`s are available on stable Rust, this will no longer be necessary.
185     /// Thus, these fields are *not* considered stable public API, and they may
186     /// change warning. Do not rely on any fields on `Identifier`. When
187     /// constructing new `Identifier`s, use the `identify_callsite!` macro
188     /// instead.
189     #[doc(hidden)]
190     pub &'static dyn Callsite,
191 );
192 
193 /// A default [`Callsite`] implementation.
194 #[derive(Debug)]
195 pub struct DefaultCallsite {
196     interest: AtomicU8,
197     registration: AtomicU8,
198     meta: &'static Metadata<'static>,
199     next: AtomicPtr<Self>,
200 }
201 
202 /// Clear and reregister interest on every [`Callsite`]
203 ///
204 /// This function is intended for runtime reconfiguration of filters on traces
205 /// when the filter recalculation is much less frequent than trace events are.
206 /// The alternative is to have the [`Subscriber`] that supports runtime
207 /// reconfiguration of filters always return [`Interest::sometimes()`] so that
208 /// [`enabled`] is evaluated for every event.
209 ///
210 /// This function will also re-compute the global maximum level as determined by
211 /// the [`max_level_hint`] method. If a [`Subscriber`]
212 /// implementation changes the value returned by its `max_level_hint`
213 /// implementation at runtime, then it **must** call this function after that
214 /// value changes, in order for the change to be reflected.
215 ///
216 /// See the [documentation on callsite interest caching][cache-docs] for
217 /// additional information on this function's usage.
218 ///
219 /// [`max_level_hint`]: super::subscriber::Subscriber::max_level_hint
220 /// [`Callsite`]: super::callsite::Callsite
221 /// [`enabled`]: super::subscriber::Subscriber#tymethod.enabled
222 /// [`Interest::sometimes()`]: super::subscriber::Interest::sometimes
223 /// [`Subscriber`]: super::subscriber::Subscriber
224 /// [cache-docs]: crate::callsite#rebuilding-cached-interest
rebuild_interest_cache()225 pub fn rebuild_interest_cache() {
226     CALLSITES.rebuild_interest(DISPATCHERS.rebuilder());
227 }
228 
229 /// Register a new [`Callsite`] with the global registry.
230 ///
231 /// This should be called once per callsite after the callsite has been
232 /// constructed.
233 ///
234 /// See the [documentation on callsite registration][reg-docs] for details
235 /// on the global callsite registry.
236 ///
237 /// [`Callsite`]: crate::callsite::Callsite
238 /// [reg-docs]: crate::callsite#registering-callsites
register(callsite: &'static dyn Callsite)239 pub fn register(callsite: &'static dyn Callsite) {
240     rebuild_callsite_interest(callsite, &DISPATCHERS.rebuilder());
241 
242     // Is this a `DefaultCallsite`? If so, use the fancy linked list!
243     if callsite.private_type_id(private::Private(())).0 == TypeId::of::<DefaultCallsite>() {
244         let callsite = unsafe {
245             // Safety: the pointer cast is safe because the type id of the
246             // provided callsite matches that of the target type for the cast
247             // (`DefaultCallsite`). Because user implementations of `Callsite`
248             // cannot override `private_type_id`, we can trust that the callsite
249             // is not lying about its type ID.
250             &*(callsite as *const dyn Callsite as *const DefaultCallsite)
251         };
252         CALLSITES.push_default(callsite);
253         return;
254     }
255 
256     CALLSITES.push_dyn(callsite);
257 }
258 
259 static CALLSITES: Callsites = Callsites {
260     list_head: AtomicPtr::new(ptr::null_mut()),
261     has_locked_callsites: AtomicBool::new(false),
262 };
263 
264 static DISPATCHERS: Dispatchers = Dispatchers::new();
265 
266 static LOCKED_CALLSITES: Lazy<Mutex<Vec<&'static dyn Callsite>>> = Lazy::new(Default::default);
267 
268 struct Callsites {
269     list_head: AtomicPtr<DefaultCallsite>,
270     has_locked_callsites: AtomicBool,
271 }
272 
273 // === impl DefaultCallsite ===
274 
275 impl DefaultCallsite {
276     const UNREGISTERED: u8 = 0;
277     const REGISTERING: u8 = 1;
278     const REGISTERED: u8 = 2;
279 
280     const INTEREST_NEVER: u8 = 0;
281     const INTEREST_SOMETIMES: u8 = 1;
282     const INTEREST_ALWAYS: u8 = 2;
283 
284     /// Returns a new `DefaultCallsite` with the specified `Metadata`.
new(meta: &'static Metadata<'static>) -> Self285     pub const fn new(meta: &'static Metadata<'static>) -> Self {
286         Self {
287             interest: AtomicU8::new(0xFF),
288             meta,
289             next: AtomicPtr::new(ptr::null_mut()),
290             registration: AtomicU8::new(Self::UNREGISTERED),
291         }
292     }
293 
294     /// Registers this callsite with the global callsite registry.
295     ///
296     /// If the callsite is already registered, this does nothing. When using
297     /// [`DefaultCallsite`], this method should be preferred over
298     /// [`tracing_core::callsite::register`], as it ensures that the callsite is
299     /// only registered a single time.
300     ///
301     /// Other callsite implementations will generally ensure that
302     /// callsites are not re-registered through another mechanism.
303     ///
304     /// See the [documentation on callsite registration][reg-docs] for details
305     /// on the global callsite registry.
306     ///
307     /// [`Callsite`]: crate::callsite::Callsite
308     /// [reg-docs]: crate::callsite#registering-callsites
309     #[inline(never)]
310     // This only happens once (or if the cached interest value was corrupted).
311     #[cold]
register(&'static self) -> Interest312     pub fn register(&'static self) -> Interest {
313         // Attempt to advance the registration state to `REGISTERING`...
314         match self.registration.compare_exchange(
315             Self::UNREGISTERED,
316             Self::REGISTERING,
317             Ordering::AcqRel,
318             Ordering::Acquire,
319         ) {
320             Ok(_) => {
321                 // Okay, we advanced the state, try to register the callsite.
322                 rebuild_callsite_interest(self, &DISPATCHERS.rebuilder());
323                 CALLSITES.push_default(self);
324                 self.registration.store(Self::REGISTERED, Ordering::Release);
325             }
326             // Great, the callsite is already registered! Just load its
327             // previous cached interest.
328             Err(Self::REGISTERED) => {}
329             // Someone else is registering...
330             Err(_state) => {
331                 debug_assert_eq!(
332                     _state,
333                     Self::REGISTERING,
334                     "weird callsite registration state"
335                 );
336                 // Just hit `enabled` this time.
337                 return Interest::sometimes();
338             }
339         }
340 
341         match self.interest.load(Ordering::Relaxed) {
342             Self::INTEREST_NEVER => Interest::never(),
343             Self::INTEREST_ALWAYS => Interest::always(),
344             _ => Interest::sometimes(),
345         }
346     }
347 
348     /// Returns the callsite's cached `Interest`, or registers it for the
349     /// first time if it has not yet been registered.
350     #[inline]
interest(&'static self) -> Interest351     pub fn interest(&'static self) -> Interest {
352         match self.interest.load(Ordering::Relaxed) {
353             Self::INTEREST_NEVER => Interest::never(),
354             Self::INTEREST_SOMETIMES => Interest::sometimes(),
355             Self::INTEREST_ALWAYS => Interest::always(),
356             _ => self.register(),
357         }
358     }
359 }
360 
361 impl Callsite for DefaultCallsite {
set_interest(&self, interest: Interest)362     fn set_interest(&self, interest: Interest) {
363         let interest = match () {
364             _ if interest.is_never() => Self::INTEREST_NEVER,
365             _ if interest.is_always() => Self::INTEREST_ALWAYS,
366             _ => Self::INTEREST_SOMETIMES,
367         };
368         self.interest.store(interest, Ordering::SeqCst);
369     }
370 
371     #[inline(always)]
metadata(&self) -> &Metadata<'static>372     fn metadata(&self) -> &Metadata<'static> {
373         self.meta
374     }
375 }
376 
377 // ===== impl Identifier =====
378 
379 impl PartialEq for Identifier {
eq(&self, other: &Identifier) -> bool380     fn eq(&self, other: &Identifier) -> bool {
381         core::ptr::eq(
382             self.0 as *const _ as *const (),
383             other.0 as *const _ as *const (),
384         )
385     }
386 }
387 
388 impl Eq for Identifier {}
389 
390 impl fmt::Debug for Identifier {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result391     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
392         write!(f, "Identifier({:p})", self.0)
393     }
394 }
395 
396 impl Hash for Identifier {
hash<H>(&self, state: &mut H) where H: Hasher,397     fn hash<H>(&self, state: &mut H)
398     where
399         H: Hasher,
400     {
401         (self.0 as *const dyn Callsite).hash(state)
402     }
403 }
404 
405 // === impl Callsites ===
406 
407 impl Callsites {
408     /// Rebuild `Interest`s for all callsites in the registry.
409     ///
410     /// This also re-computes the max level hint.
rebuild_interest(&self, dispatchers: dispatchers::Rebuilder<'_>)411     fn rebuild_interest(&self, dispatchers: dispatchers::Rebuilder<'_>) {
412         let mut max_level = LevelFilter::OFF;
413         dispatchers.for_each(|dispatch| {
414             // If the subscriber did not provide a max level hint, assume
415             // that it may enable every level.
416             let level_hint = dispatch.max_level_hint().unwrap_or(LevelFilter::TRACE);
417             if level_hint > max_level {
418                 max_level = level_hint;
419             }
420         });
421 
422         self.for_each(|callsite| {
423             rebuild_callsite_interest(callsite, &dispatchers);
424         });
425         LevelFilter::set_max(max_level);
426     }
427 
428     /// Push a `dyn Callsite` trait object to the callsite registry.
429     ///
430     /// This will attempt to lock the callsites vector.
push_dyn(&self, callsite: &'static dyn Callsite)431     fn push_dyn(&self, callsite: &'static dyn Callsite) {
432         let mut lock = LOCKED_CALLSITES.lock().unwrap();
433         self.has_locked_callsites.store(true, Ordering::Release);
434         lock.push(callsite);
435     }
436 
437     /// Push a `DefaultCallsite` to the callsite registry.
438     ///
439     /// If we know the callsite being pushed is a `DefaultCallsite`, we can push
440     /// it to the linked list without having to acquire a lock.
push_default(&self, callsite: &'static DefaultCallsite)441     fn push_default(&self, callsite: &'static DefaultCallsite) {
442         let mut head = self.list_head.load(Ordering::Acquire);
443 
444         loop {
445             callsite.next.store(head, Ordering::Release);
446 
447             assert_ne!(
448                 callsite as *const _, head,
449                 "Attempted to register a `DefaultCallsite` that already exists! \
450                 This will cause an infinite loop when attempting to read from the \
451                 callsite cache. This is likely a bug! You should only need to call \
452                 `DefaultCallsite::register` once per `DefaultCallsite`."
453             );
454 
455             match self.list_head.compare_exchange(
456                 head,
457                 callsite as *const _ as *mut _,
458                 Ordering::AcqRel,
459                 Ordering::Acquire,
460             ) {
461                 Ok(_) => {
462                     break;
463                 }
464                 Err(current) => head = current,
465             }
466         }
467     }
468 
469     /// Invokes the provided closure `f` with each callsite in the registry.
for_each(&self, mut f: impl FnMut(&'static dyn Callsite))470     fn for_each(&self, mut f: impl FnMut(&'static dyn Callsite)) {
471         let mut head = self.list_head.load(Ordering::Acquire);
472 
473         while let Some(cs) = unsafe { head.as_ref() } {
474             f(cs);
475 
476             head = cs.next.load(Ordering::Acquire);
477         }
478 
479         if self.has_locked_callsites.load(Ordering::Acquire) {
480             let locked = LOCKED_CALLSITES.lock().unwrap();
481             for &cs in locked.iter() {
482                 f(cs);
483             }
484         }
485     }
486 }
487 
register_dispatch(dispatch: &Dispatch)488 pub(crate) fn register_dispatch(dispatch: &Dispatch) {
489     let dispatchers = DISPATCHERS.register_dispatch(dispatch);
490     dispatch.subscriber().on_register_dispatch(dispatch);
491     CALLSITES.rebuild_interest(dispatchers);
492 }
493 
rebuild_callsite_interest( callsite: &'static dyn Callsite, dispatchers: &dispatchers::Rebuilder<'_>, )494 fn rebuild_callsite_interest(
495     callsite: &'static dyn Callsite,
496     dispatchers: &dispatchers::Rebuilder<'_>,
497 ) {
498     let meta = callsite.metadata();
499 
500     let mut interest = None;
501     dispatchers.for_each(|dispatch| {
502         let this_interest = dispatch.register_callsite(meta);
503         interest = match interest.take() {
504             None => Some(this_interest),
505             Some(that_interest) => Some(that_interest.and(this_interest)),
506         }
507     });
508 
509     let interest = interest.unwrap_or_else(Interest::never);
510     callsite.set_interest(interest)
511 }
512 
513 mod private {
514     /// Don't call this function, it's private.
515     #[allow(missing_debug_implementations)]
516     pub struct Private<T>(pub(crate) T);
517 }
518 
519 #[cfg(feature = "std")]
520 mod dispatchers {
521     use crate::{dispatcher, lazy::Lazy};
522     use std::sync::{
523         atomic::{AtomicBool, Ordering},
524         RwLock, RwLockReadGuard, RwLockWriteGuard,
525     };
526 
527     pub(super) struct Dispatchers {
528         has_just_one: AtomicBool,
529     }
530 
531     static LOCKED_DISPATCHERS: Lazy<RwLock<Vec<dispatcher::Registrar>>> =
532         Lazy::new(Default::default);
533 
534     pub(super) enum Rebuilder<'a> {
535         JustOne,
536         Read(RwLockReadGuard<'a, Vec<dispatcher::Registrar>>),
537         Write(RwLockWriteGuard<'a, Vec<dispatcher::Registrar>>),
538     }
539 
540     impl Dispatchers {
new() -> Self541         pub(super) const fn new() -> Self {
542             Self {
543                 has_just_one: AtomicBool::new(true),
544             }
545         }
546 
rebuilder(&self) -> Rebuilder<'_>547         pub(super) fn rebuilder(&self) -> Rebuilder<'_> {
548             if self.has_just_one.load(Ordering::SeqCst) {
549                 return Rebuilder::JustOne;
550             }
551             Rebuilder::Read(LOCKED_DISPATCHERS.read().unwrap())
552         }
553 
register_dispatch(&self, dispatch: &dispatcher::Dispatch) -> Rebuilder<'_>554         pub(super) fn register_dispatch(&self, dispatch: &dispatcher::Dispatch) -> Rebuilder<'_> {
555             let mut dispatchers = LOCKED_DISPATCHERS.write().unwrap();
556             dispatchers.retain(|d| d.upgrade().is_some());
557             dispatchers.push(dispatch.registrar());
558             self.has_just_one
559                 .store(dispatchers.len() <= 1, Ordering::SeqCst);
560             Rebuilder::Write(dispatchers)
561         }
562     }
563 
564     impl Rebuilder<'_> {
for_each(&self, mut f: impl FnMut(&dispatcher::Dispatch))565         pub(super) fn for_each(&self, mut f: impl FnMut(&dispatcher::Dispatch)) {
566             let iter = match self {
567                 Rebuilder::JustOne => {
568                     dispatcher::get_default(f);
569                     return;
570                 }
571                 Rebuilder::Read(vec) => vec.iter(),
572                 Rebuilder::Write(vec) => vec.iter(),
573             };
574             iter.filter_map(dispatcher::Registrar::upgrade)
575                 .for_each(|dispatch| f(&dispatch))
576         }
577     }
578 }
579 
580 #[cfg(not(feature = "std"))]
581 mod dispatchers {
582     use crate::dispatcher;
583 
584     pub(super) struct Dispatchers(());
585     pub(super) struct Rebuilder<'a>(Option<&'a dispatcher::Dispatch>);
586 
587     impl Dispatchers {
new() -> Self588         pub(super) const fn new() -> Self {
589             Self(())
590         }
591 
rebuilder(&self) -> Rebuilder<'_>592         pub(super) fn rebuilder(&self) -> Rebuilder<'_> {
593             Rebuilder(None)
594         }
595 
register_dispatch<'dispatch>( &self, dispatch: &'dispatch dispatcher::Dispatch, ) -> Rebuilder<'dispatch>596         pub(super) fn register_dispatch<'dispatch>(
597             &self,
598             dispatch: &'dispatch dispatcher::Dispatch,
599         ) -> Rebuilder<'dispatch> {
600             // nop; on no_std, there can only ever be one dispatcher
601             Rebuilder(Some(dispatch))
602         }
603     }
604 
605     impl Rebuilder<'_> {
606         #[inline]
for_each(&self, mut f: impl FnMut(&dispatcher::Dispatch))607         pub(super) fn for_each(&self, mut f: impl FnMut(&dispatcher::Dispatch)) {
608             if let Some(dispatch) = self.0 {
609                 // we are rebuilding the interest cache because a new dispatcher
610                 // is about to be set. on `no_std`, this should only happen
611                 // once, because the new dispatcher will be the global default.
612                 f(dispatch)
613             } else {
614                 // otherwise, we are rebuilding the cache because the subscriber
615                 // configuration changed, so use the global default.
616                 // on no_std, there can only ever be one dispatcher
617                 dispatcher::get_default(f)
618             }
619         }
620     }
621 }
622