]>
Commit | Line | Data |
---|---|---|
f035d41b XL |
1 | //! Callsites represent the source locations from which spans or events |
2 | //! originate. | |
923072b8 FG |
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 | |
f035d41b | 101 | use crate::stdlib::{ |
923072b8 | 102 | any::TypeId, |
f035d41b XL |
103 | fmt, |
104 | hash::{Hash, Hasher}, | |
923072b8 FG |
105 | ptr, |
106 | sync::{ | |
107 | atomic::{AtomicBool, AtomicPtr, AtomicU8, Ordering}, | |
108 | Mutex, | |
109 | }, | |
f035d41b XL |
110 | vec::Vec, |
111 | }; | |
112 | use crate::{ | |
923072b8 | 113 | dispatcher::Dispatch, |
064997fb | 114 | lazy::Lazy, |
3dfed10e | 115 | metadata::{LevelFilter, Metadata}, |
f035d41b | 116 | subscriber::Interest, |
f035d41b XL |
117 | }; |
118 | ||
923072b8 | 119 | use self::dispatchers::Dispatchers; |
f035d41b XL |
120 | |
121 | /// Trait implemented by callsites. | |
122 | /// | |
29967ef6 | 123 | /// These functions are only intended to be called by the callsite registry, which |
f035d41b | 124 | /// correctly handles determining the common interest between all subscribers. |
923072b8 FG |
125 | /// |
126 | /// See the [module-level documentation](crate::callsite) for details on | |
127 | /// callsites. | |
f035d41b XL |
128 | pub trait Callsite: Sync { |
129 | /// Sets the [`Interest`] for this callsite. | |
130 | /// | |
923072b8 FG |
131 | /// See the [documentation on callsite interest caching][cache-docs] for |
132 | /// details. | |
133 | /// | |
04454e1e | 134 | /// [`Interest`]: super::subscriber::Interest |
923072b8 | 135 | /// [cache-docs]: crate::callsite#performing-static-filtering |
f035d41b XL |
136 | fn set_interest(&self, interest: Interest); |
137 | ||
138 | /// Returns the [metadata] associated with the callsite. | |
139 | /// | |
f2b60f7d FG |
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 | /// | |
04454e1e | 149 | /// [metadata]: super::metadata::Metadata |
f035d41b | 150 | fn metadata(&self) -> &Metadata<'_>; |
923072b8 FG |
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] | |
167 | fn private_type_id(&self, _: private::Private<()>) -> private::Private<TypeId> | |
168 | where | |
169 | Self: 'static, | |
170 | { | |
171 | private::Private(TypeId::of::<Self>()) | |
172 | } | |
f035d41b XL |
173 | } |
174 | ||
175 | /// Uniquely identifies a [`Callsite`] | |
176 | /// | |
177 | /// Two `Identifier`s are equal if they both refer to the same callsite. | |
178 | /// | |
04454e1e | 179 | /// [`Callsite`]: super::callsite::Callsite |
f035d41b XL |
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 | |
04454e1e FG |
187 | /// constructing new `Identifier`s, use the `identify_callsite!` macro |
188 | /// instead. | |
f035d41b XL |
189 | #[doc(hidden)] |
190 | pub &'static dyn Callsite, | |
191 | ); | |
192 | ||
923072b8 FG |
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 | ||
f035d41b XL |
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 | /// | |
3dfed10e | 210 | /// This function will also re-compute the global maximum level as determined by |
29967ef6 | 211 | /// the [`max_level_hint`] method. If a [`Subscriber`] |
3dfed10e XL |
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 | /// | |
923072b8 FG |
216 | /// See the [documentation on callsite interest caching][cache-docs] for |
217 | /// additional information on this function's usage. | |
218 | /// | |
04454e1e FG |
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 | |
923072b8 | 224 | /// [cache-docs]: crate::callsite#rebuilding-cached-interest |
f035d41b | 225 | pub fn rebuild_interest_cache() { |
923072b8 | 226 | CALLSITES.rebuild_interest(DISPATCHERS.rebuilder()); |
f035d41b XL |
227 | } |
228 | ||
923072b8 | 229 | /// Register a new [`Callsite`] with the global registry. |
f035d41b XL |
230 | /// |
231 | /// This should be called once per callsite after the callsite has been | |
232 | /// constructed. | |
923072b8 FG |
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 | |
f035d41b | 239 | pub fn register(callsite: &'static dyn Callsite) { |
923072b8 FG |
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); | |
f035d41b XL |
257 | } |
258 | ||
923072b8 FG |
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 | ||
064997fb | 266 | static LOCKED_CALLSITES: Lazy<Mutex<Vec<&'static dyn Callsite>>> = Lazy::new(Default::default); |
923072b8 FG |
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`. | |
285 | 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] | |
312 | 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] | |
351 | 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 { | |
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)] | |
372 | fn metadata(&self) -> &Metadata<'static> { | |
373 | self.meta | |
374 | } | |
f035d41b XL |
375 | } |
376 | ||
377 | // ===== impl Identifier ===== | |
378 | ||
379 | impl PartialEq for Identifier { | |
380 | fn eq(&self, other: &Identifier) -> bool { | |
17df50a5 XL |
381 | core::ptr::eq( |
382 | self.0 as *const _ as *const (), | |
383 | other.0 as *const _ as *const (), | |
384 | ) | |
f035d41b XL |
385 | } |
386 | } | |
387 | ||
388 | impl Eq for Identifier {} | |
389 | ||
390 | impl fmt::Debug for Identifier { | |
391 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
392 | write!(f, "Identifier({:p})", self.0) | |
393 | } | |
394 | } | |
395 | ||
396 | impl Hash for Identifier { | |
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 | } | |
923072b8 FG |
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. | |
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. | |
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. | |
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. | |
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 | ||
488 | pub(crate) fn register_dispatch(dispatch: &Dispatch) { | |
489 | let dispatchers = DISPATCHERS.register_dispatch(dispatch); | |
2b03887a | 490 | dispatch.subscriber().on_register_dispatch(dispatch); |
923072b8 FG |
491 | CALLSITES.rebuild_interest(dispatchers); |
492 | } | |
493 | ||
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 { | |
064997fb | 521 | use crate::{dispatcher, lazy::Lazy}; |
923072b8 FG |
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 { | |
541 | pub(super) const fn new() -> Self { | |
542 | Self { | |
543 | has_just_one: AtomicBool::new(true), | |
544 | } | |
545 | } | |
546 | ||
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 | ||
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<'_> { | |
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 { | |
588 | pub(super) const fn new() -> Self { | |
589 | Self(()) | |
590 | } | |
591 | ||
592 | pub(super) fn rebuilder(&self) -> Rebuilder<'_> { | |
593 | Rebuilder(None) | |
594 | } | |
595 | ||
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] | |
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 | } |