use std::num::NonZeroU64;
use std::sync::atomic::{Ordering, AtomicU64, AtomicPtr};
use once_cell::sync::Lazy;
static NEXT_CALL_SITE_ID: AtomicU64 = AtomicU64::new(1);
static REGISTRY: Lazy<Registry> = Lazy::new(|| {
Registry {
head: AtomicPtr::new(std::ptr::null_mut()),
}
});
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct CallSiteId(NonZeroU64);
impl CallSiteId {
pub(crate) fn new(value: u64) -> CallSiteId {
CallSiteId(std::num::NonZeroU64::new(value).expect("got a zero value for span id"))
}
}
#[derive(Debug)]
pub struct CallSite {
id: CallSiteId,
pub(crate) depth: &'static std::thread::LocalKey<std::cell::Cell<usize>>,
name: &'static str,
module_path: &'static str,
file: &'static str,
line: u32,
next: AtomicPtr<CallSite>,
}
impl CallSite {
#[doc(hidden)]
pub fn new(
depth: &'static std::thread::LocalKey<std::cell::Cell<usize>>,
name: &'static str,
module_path: &'static str,
file: &'static str,
line: u32
) -> CallSite {
let id = CallSiteId::new(NEXT_CALL_SITE_ID.fetch_add(1, Ordering::SeqCst));
let next = AtomicPtr::new(std::ptr::null_mut());
CallSite { id, depth, name, module_path, file, line, next }
}
pub(crate) fn id(&self) -> CallSiteId {
self.id
}
pub fn name(&self) -> &str {
self.name
}
pub fn module_path(&self) -> &str {
self.module_path
}
pub fn file(&self) -> &str {
self.file
}
pub fn line(&self) -> u32 {
self.line
}
pub fn full_name(&self) -> String {
let mut name = self.module_path.to_owned();
name += "::";
if self.name.contains(' ') {
name += "{";
name += self.name;
name += "}";
} else {
name += self.name;
}
return name;
}
}
struct Registry {
head: AtomicPtr<CallSite>,
}
impl Registry {
fn register(&self, callsite: &'static CallSite) {
let mut head = self.head.load(Ordering::Acquire);
loop {
callsite.next.store(head, Ordering::Release);
assert_ne!(
callsite as *const _, head,
"Attempted to register a `Callsite` that already exists! \
This will cause an infinite loop when attempting to read from the \
callsite registry."
);
match self.head.compare_exchange(
head,
callsite as *const _ as *mut _,
Ordering::AcqRel,
Ordering::Acquire,
) {
Ok(_) => {
break;
}
Err(current) => head = current,
}
}
}
fn for_each(&self, mut f: impl FnMut(&'static CallSite)) {
let mut head = self.head.load(Ordering::Acquire);
while let Some(registered) = unsafe { head.as_ref() } {
f(registered);
head = registered.next.load(Ordering::Acquire);
}
}
}
#[doc(hidden)]
pub fn register_callsite(callsite: &'static CallSite) {
REGISTRY.register(callsite);
}
pub fn traverse_registered_callsite(function: impl FnMut(&'static CallSite)) {
REGISTRY.for_each(function);
}