1use std::num::NonZeroU64;
2use std::sync::atomic::{Ordering, AtomicU64, AtomicPtr};
3
4use once_cell::sync::Lazy;
5
6static NEXT_CALL_SITE_ID: AtomicU64 = AtomicU64::new(1);
8static REGISTRY: Lazy<Registry> = Lazy::new(|| {
10 Registry {
11 head: AtomicPtr::new(std::ptr::null_mut()),
12 }
13});
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
18pub struct CallSiteId(NonZeroU64);
19
20impl CallSiteId {
21 pub(crate) fn new(value: u64) -> CallSiteId {
22 CallSiteId(std::num::NonZeroU64::new(value).expect("got a zero value for span id"))
23 }
24}
25
26#[derive(Debug)]
32pub struct CallSite {
33 id: CallSiteId,
35 pub(crate) depth: &'static std::thread::LocalKey<std::cell::Cell<usize>>,
37 name: &'static str,
39 module_path: &'static str,
41 file: &'static str,
43 line: u32,
45 next: AtomicPtr<CallSite>,
49}
50
51impl CallSite {
52 #[doc(hidden)]
56 pub fn new(
57 depth: &'static std::thread::LocalKey<std::cell::Cell<usize>>,
58 name: &'static str,
59 module_path: &'static str,
60 file: &'static str,
61 line: u32
62 ) -> CallSite {
63 let id = CallSiteId::new(NEXT_CALL_SITE_ID.fetch_add(1, Ordering::SeqCst));
64 let next = AtomicPtr::new(std::ptr::null_mut());
65 CallSite { id, depth, name, module_path, file, line, next }
66 }
67
68 pub(crate) fn id(&self) -> CallSiteId {
69 self.id
70 }
71
72 pub fn name(&self) -> &str {
74 self.name
75 }
76
77 pub fn module_path(&self) -> &str {
79 self.module_path
80 }
81
82 pub fn file(&self) -> &str {
84 self.file
85 }
86
87 pub fn line(&self) -> u32 {
89 self.line
90 }
91
92 pub fn full_name(&self) -> String {
95 let mut name = self.module_path.to_owned();
96 name += "::";
97
98 if self.name.contains(' ') {
99 name += "{";
100 name += self.name;
101 name += "}";
102 } else {
103 name += self.name;
104 }
105
106 return name;
107 }
108}
109
110struct Registry {
113 head: AtomicPtr<CallSite>,
114}
115
116impl Registry {
117 fn register(&self, callsite: &'static CallSite) {
119 let mut head = self.head.load(Ordering::Acquire);
120
121 loop {
122 callsite.next.store(head, Ordering::Release);
123
124 assert_ne!(
125 callsite as *const _, head,
126 "Attempted to register a `Callsite` that already exists! \
127 This will cause an infinite loop when attempting to read from the \
128 callsite registry."
129 );
130
131 match self.head.compare_exchange(
132 head,
133 callsite as *const _ as *mut _,
134 Ordering::AcqRel,
135 Ordering::Acquire,
136 ) {
137 Ok(_) => {
138 break;
139 }
140 Err(current) => head = current,
141 }
142 }
143 }
144
145 fn for_each(&self, mut f: impl FnMut(&'static CallSite)) {
147 let mut head = self.head.load(Ordering::Acquire);
148
149 while let Some(registered) = unsafe { head.as_ref() } {
150 f(registered);
151 head = registered.next.load(Ordering::Acquire);
152 }
153 }
154}
155
156#[doc(hidden)]
159pub fn register_callsite(callsite: &'static CallSite) {
160 REGISTRY.register(callsite);
161}
162
163pub fn traverse_registered_callsite(function: impl FnMut(&'static CallSite)) {
174 REGISTRY.for_each(function);
175}