use std::{
cell::RefCell,
fmt, mem, str,
sync::atomic::{self, AtomicUsize, Ordering},
};
use owning_ref::OwningHandle;
use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard};
pub(crate) use tracing_core::span::{Attributes, Current, Id, Record};
use tracing_core::{dispatcher, Metadata};
pub struct Span<'a> {
lock: OwningHandle<RwLockReadGuard<'a, Slab>, RwLockReadGuard<'a, Slot>>,
}
#[derive(Debug)]
pub struct Context<'a, N> {
store: &'a Store,
new_visitor: &'a N,
}
#[derive(Debug)]
pub(crate) struct Store {
inner: RwLock<Slab>,
next: AtomicUsize,
}
#[derive(Debug)]
pub(crate) struct Data {
parent: Option<Id>,
metadata: &'static Metadata<'static>,
ref_count: AtomicUsize,
is_empty: bool,
}
#[derive(Debug)]
struct Slab {
slab: Vec<RwLock<Slot>>,
}
#[derive(Debug)]
struct Slot {
fields: String,
span: State,
}
#[derive(Debug)]
enum State {
Full(Data),
Empty(usize),
}
thread_local! {
static CONTEXT: RefCell<Vec<Id>> = RefCell::new(vec![]);
}
macro_rules! debug_panic {
($($args:tt)*) => {
#[cfg(debug_assertions)] {
if !std::thread::panicking() {
panic!($($args)*)
}
}
}
}
impl<'a> Span<'a> {
pub fn name(&self) -> &'static str {
match self.lock.span {
State::Full(ref data) => data.metadata.name(),
State::Empty(_) => unreachable!(),
}
}
pub fn metadata(&self) -> &'static Metadata<'static> {
match self.lock.span {
State::Full(ref data) => data.metadata,
State::Empty(_) => unreachable!(),
}
}
pub fn fields(&self) -> &str {
self.lock.fields.as_ref()
}
pub fn parent(&self) -> Option<&Id> {
match self.lock.span {
State::Full(ref data) => data.parent.as_ref(),
State::Empty(_) => unreachable!(),
}
}
#[inline(always)]
fn with_parent<'store, F, E>(
self,
my_id: &Id,
last_id: Option<&Id>,
f: &mut F,
store: &'store Store,
) -> Result<(), E>
where
F: FnMut(&Id, Span<'_>) -> Result<(), E>,
{
if let Some(parent_id) = self.parent() {
if Some(parent_id) != last_id {
if let Some(parent) = store.get(parent_id) {
parent.with_parent(parent_id, Some(my_id), f, store)?;
} else {
debug_panic!("missing span for {:?}; this is a bug", parent_id);
}
}
}
f(my_id, self)
}
}
impl<'a> fmt::Debug for Span<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Span")
.field("name", &self.name())
.field("parent", &self.parent())
.field("metadata", self.metadata())
.field("fields", &self.fields())
.finish()
}
}
impl<'a, N> Context<'a, N> {
pub fn visit_spans<F, E>(&self, mut f: F) -> Result<(), E>
where
F: FnMut(&Id, Span<'_>) -> Result<(), E>,
{
CONTEXT
.try_with(|current| {
if let Some(id) = current.borrow().last() {
if let Some(span) = self.store.get(id) {
return span.with_parent(id, None, &mut f, self.store);
} else {
debug_panic!("missing span for {:?}; this is a bug", id);
}
}
Ok(())
})
.unwrap_or(Ok(()))
}
pub fn with_current<F, R>(&self, f: F) -> Option<R>
where
F: FnOnce((&Id, Span<'_>)) -> R,
{
CONTEXT
.try_with(|current| {
if let Some(id) = current.borrow().last() {
if let Some(span) = self.store.get(id) {
return Some(f((id, span)));
} else {
debug_panic!("missing span for {:?}, this is a bug", id);
}
}
None
})
.ok()?
}
pub(crate) fn new(store: &'a Store, new_visitor: &'a N) -> Self {
Self { store, new_visitor }
}
pub fn new_visitor<'writer>(
&self,
writer: &'writer mut dyn fmt::Write,
is_empty: bool,
) -> N::Visitor
where
N: super::NewVisitor<'writer>,
{
self.new_visitor.make(writer, is_empty)
}
}
#[inline]
fn idx_to_id(idx: usize) -> Id {
Id::from_u64(idx as u64 + 1)
}
#[inline]
fn id_to_idx(id: &Id) -> usize {
id.into_u64() as usize - 1
}
impl Store {
pub(crate) fn with_capacity(capacity: usize) -> Self {
Store {
inner: RwLock::new(Slab {
slab: Vec::with_capacity(capacity),
}),
next: AtomicUsize::new(0),
}
}
#[inline]
pub(crate) fn current(&self) -> Option<Id> {
CONTEXT
.try_with(|current| current.borrow().last().map(|span| self.clone_span(span)))
.ok()?
}
pub(crate) fn push(&self, id: &Id) {
let _ = CONTEXT.try_with(|current| {
let mut current = current.borrow_mut();
if current.contains(id) {
return;
}
current.push(self.clone_span(id));
});
}
pub(crate) fn pop(&self, expected_id: &Id) {
let id = CONTEXT
.try_with(|current| {
let mut current = current.borrow_mut();
if current.last() == Some(expected_id) {
current.pop()
} else {
None
}
})
.ok()
.and_then(|i| i);
if let Some(id) = id {
let _ = self.drop_span(id);
}
}
#[inline]
pub(crate) fn new_span<N>(&self, attrs: &Attributes<'_>, new_visitor: &N) -> Id
where
N: for<'a> super::NewVisitor<'a>,
{
let mut span = Some(Data::new(attrs, self));
loop {
let head = self.next.load(Ordering::Relaxed);
{
let this = self.inner.read();
if head < this.slab.len() {
if let Some(mut slot) = this.slab[head].try_write() {
if let Some(next) = slot.next() {
if self.next.compare_and_swap(head, next, Ordering::Release) == head {
slot.fill(span.take().unwrap(), attrs, new_visitor);
return idx_to_id(head);
}
}
}
atomic::spin_loop_hint();
continue;
}
}
if let Some(mut this) = self.inner.try_write() {
let len = this.slab.len();
let slot = Slot::new(span.take().unwrap(), attrs, new_visitor);
this.slab.push(RwLock::new(slot));
self.next.store(len + 1, Ordering::Release);
return idx_to_id(len);
}
atomic::spin_loop_hint();
}
}
#[inline]
pub(crate) fn get(&self, id: &Id) -> Option<Span<'_>> {
let lock = OwningHandle::try_new(self.inner.read(), |slab| {
unsafe { &*slab }.read_slot(id_to_idx(id)).ok_or(())
})
.ok()?;
Some(Span { lock })
}
#[inline]
pub(crate) fn record<N>(&self, id: &Id, fields: &Record<'_>, new_recorder: &N)
where
N: for<'a> super::NewVisitor<'a>,
{
let slab = self.inner.read();
let slot = slab.write_slot(id_to_idx(id));
if let Some(mut slot) = slot {
slot.record(fields, new_recorder);
}
}
pub(crate) fn drop_span(&self, id: Id) -> bool {
let this = self.inner.read();
let idx = id_to_idx(&id);
if !this
.slab
.get(idx)
.map(|span| span.read().drop_ref())
.unwrap_or_else(|| {
debug_panic!("tried to drop {:?} but it no longer exists!", id);
false
})
{
return false;
}
atomic::fence(Ordering::Acquire);
this.remove(&self.next, idx);
true
}
pub(crate) fn clone_span(&self, id: &Id) -> Id {
let this = self.inner.read();
let idx = id_to_idx(id);
if let Some(span) = this.slab.get(idx).map(|span| span.read()) {
span.clone_ref();
} else {
debug_panic!(
"tried to clone {:?}, but no span exists with that ID. this is a bug!",
id
);
}
id.clone()
}
}
impl Data {
pub(crate) fn new(attrs: &Attributes<'_>, store: &Store) -> Self {
let parent = if attrs.is_root() {
None
} else if attrs.is_contextual() {
store.current()
} else {
attrs.parent().map(|id| store.clone_span(id))
};
Self {
metadata: attrs.metadata(),
parent,
ref_count: AtomicUsize::new(1),
is_empty: true,
}
}
}
impl Drop for Data {
fn drop(&mut self) {
if self.parent.is_some() {
dispatcher::get_default(|subscriber| {
if let Some(parent) = self.parent.take() {
let _ = subscriber.try_close(parent);
}
})
}
}
}
impl Slot {
fn new<N>(mut data: Data, attrs: &Attributes<'_>, new_visitor: &N) -> Self
where
N: for<'a> super::NewVisitor<'a>,
{
let mut fields = String::new();
{
let mut recorder = new_visitor.make(&mut fields, true);
attrs.record(&mut recorder);
}
if fields.is_empty() {
data.is_empty = false;
}
Self {
fields,
span: State::Full(data),
}
}
fn next(&self) -> Option<usize> {
match self.span {
State::Empty(next) => Some(next),
_ => None,
}
}
fn fill<N>(&mut self, mut data: Data, attrs: &Attributes<'_>, new_visitor: &N) -> usize
where
N: for<'a> super::NewVisitor<'a>,
{
let fields = &mut self.fields;
{
let mut recorder = new_visitor.make(fields, true);
attrs.record(&mut recorder);
}
if fields.is_empty() {
data.is_empty = false;
}
match mem::replace(&mut self.span, State::Full(data)) {
State::Empty(next) => next,
State::Full(_) => unreachable!("tried to fill a full slot"),
}
}
fn record<N>(&mut self, fields: &Record<'_>, new_visitor: &N)
where
N: for<'a> super::NewVisitor<'a>,
{
let state = &mut self.span;
let buf = &mut self.fields;
match state {
State::Empty(_) => return,
State::Full(ref mut data) => {
{
let mut recorder = new_visitor.make(buf, data.is_empty);
fields.record(&mut recorder);
}
if buf.is_empty() {
data.is_empty = false;
}
}
}
}
fn drop_ref(&self) -> bool {
match self.span {
State::Full(ref data) => data.ref_count.fetch_sub(1, Ordering::Release) == 1,
State::Empty(_) => false,
}
}
fn clone_ref(&self) {
match self.span {
State::Full(ref data) => {
let _ = data.ref_count.fetch_sub(1, Ordering::Release);
}
State::Empty(_) => {
unreachable!("tried to clone a ref to a span that no longer exists, this is a bug")
}
}
}
}
impl Slab {
#[inline]
fn write_slot(&self, idx: usize) -> Option<RwLockWriteGuard<'_, Slot>> {
self.slab.get(idx).map(RwLock::write)
}
#[inline]
fn read_slot(&self, idx: usize) -> Option<RwLockReadGuard<'_, Slot>> {
self.slab
.get(idx)
.map(RwLock::read)
.and_then(|lock| match lock.span {
State::Empty(_) => None,
State::Full(_) => Some(lock),
})
}
fn remove(&self, next: &AtomicUsize, idx: usize) -> Option<Data> {
loop {
let head = next.load(Ordering::Relaxed);
let mut slot = self.slab[idx].write();
let data = match mem::replace(&mut slot.span, State::Empty(head)) {
State::Full(data) => data,
state => {
slot.span = state;
return None;
}
};
if next.compare_and_swap(head, idx, Ordering::Release) == head {
slot.fields.clear();
return Some(data);
}
atomic::spin_loop_hint();
}
}
}