tracing_tunnel/sender/
sync.rs

1//! `Mutex`-based synchronization.
2
3use std::{collections::HashSet, sync::Mutex};
4
5use tracing_core::Metadata;
6
7use super::{metadata_id, EventSync};
8use crate::{CallSiteData, MetadataId, TracingEvent};
9
10/// Mutex-based [`EventSync`] implementation that can be used by [`TracingEventSender`](super::TracingEventSender).
11#[derive(Debug, Default)]
12pub struct Synced(Mutex<HashSet<MetadataId>>);
13
14impl EventSync for Synced {
15    fn register_callsite(
16        &self,
17        metadata: &'static Metadata<'static>,
18        sender: impl Fn(TracingEvent),
19    ) {
20        // We treat both trait methods in the same way since they may arrive out of order.
21        self.ensure_callsite_registered(metadata, sender);
22    }
23
24    /// Ensures that the callsite for the given metadata is registered.
25    /// This method is synchronous and prevents race conditions where
26    /// `NewSpan` or `NewEvent` events arrive before their `NewCallSite` dependencies.
27    fn ensure_callsite_registered(
28        &self,
29        metadata: &'static Metadata<'static>,
30        sender: impl Fn(TracingEvent),
31    ) {
32        let metadata_id = metadata_id(metadata);
33
34        // Fast path: check if already registered without lock contention
35        {
36            let registered = self.0.lock().unwrap();
37            if registered.contains(&metadata_id) {
38                return;
39            }
40        }
41
42        // Slow path: register the callsite
43        let mut registered = self.0.lock().unwrap();
44
45        // Double-check in case another thread registered it while we waited for the lock
46        if !registered.contains(&metadata_id) {
47            // Send NewCallSite event before marking as registered
48            sender(TracingEvent::NewCallSite {
49                id: metadata_id,
50                data: CallSiteData::from(metadata),
51            });
52            registered.insert(metadata_id);
53        }
54    }
55}