Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

core: remove mandatory liballoc dependency with no-std #1017

Merged
merged 21 commits into from
Oct 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion tracing-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ edition = "2018"

[features]
default = ["std"]
std = ["lazy_static"]
alloc = []
std = ["lazy_static", "alloc"]

[badges]
maintenance = { status = "actively-developed" }
Expand Down
264 changes: 164 additions & 100 deletions tracing-core/src/callsite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,16 @@ use crate::{
dispatcher::{self, Dispatch},
metadata::{LevelFilter, Metadata},
subscriber::Interest,
sync::{Mutex, MutexGuard},
};
use alloc::vec::Vec;
use core::{
fmt,
hash::{Hash, Hasher},
ptr,
sync::atomic::{AtomicPtr, Ordering},
};

lazy_static! {
static ref REGISTRY: Registry = Registry {
callsites: LinkedList::new(),
dispatchers: Mutex::new(Vec::new()),
};
}

type Dispatchers = Vec<dispatcher::Registrar>;
type Callsites = LinkedList;

struct Registry {
callsites: Callsites,
dispatchers: Mutex<Dispatchers>,
}

/// Trait implemented by callsites.
///
/// These functions are only intended to be called by the callsite registry, which
Expand Down Expand Up @@ -76,96 +61,173 @@ pub struct Registration<T = &'static dyn Callsite> {
next: AtomicPtr<Registration<T>>,
}

/// Clear and reregister interest on every [`Callsite`]
///
/// This function is intended for runtime reconfiguration of filters on traces
/// when the filter recalculation is much less frequent than trace events are.
/// The alternative is to have the [`Subscriber`] that supports runtime
/// reconfiguration of filters always return [`Interest::sometimes()`] so that
/// [`enabled`] is evaluated for every event.
///
/// This function will also re-compute the global maximum level as determined by
/// the [`max_level_hint`] method. If a [`Subscriber`]
/// implementation changes the value returned by its `max_level_hint`
/// implementation at runtime, then it **must** call this function after that
/// value changes, in order for the change to be reflected.
///
/// [`max_level_hint`]: super::subscriber::Subscriber::max_level_hint
/// [`Callsite`]: super::callsite::Callsite
/// [`enabled`]: super::subscriber::Subscriber::enabled
/// [`Interest::sometimes()`]: super::subscriber::Interest::sometimes
/// [`Subscriber`]: super::subscriber::Subscriber
pub fn rebuild_interest_cache() {
let mut dispatchers = REGISTRY.dispatchers.lock().unwrap();
let callsites = &REGISTRY.callsites;
rebuild_interest(callsites, &mut dispatchers);
}
pub(crate) use self::inner::register_dispatch;
pub use self::inner::{rebuild_interest_cache, register};

/// Register a new `Callsite` with the global registry.
///
/// This should be called once per callsite after the callsite has been
/// constructed.
pub fn register(registration: &'static Registration) {
let mut dispatchers = REGISTRY.dispatchers.lock().unwrap();
rebuild_callsite_interest(&mut dispatchers, registration.callsite);
REGISTRY.callsites.push(registration);
}
#[cfg(feature = "std")]
mod inner {
use super::*;
use std::sync::RwLock;
use std::vec::Vec;

pub(crate) fn register_dispatch(dispatch: &Dispatch) {
let mut dispatchers = REGISTRY.dispatchers.lock().unwrap();
let callsites = &REGISTRY.callsites;
type Dispatchers = Vec<dispatcher::Registrar>;
hawkw marked this conversation as resolved.
Show resolved Hide resolved

dispatchers.push(dispatch.registrar());
struct Registry {
callsites: Callsites,
dispatchers: RwLock<Dispatchers>,
}

rebuild_interest(callsites, &mut dispatchers);
}
lazy_static! {
hawkw marked this conversation as resolved.
Show resolved Hide resolved
static ref REGISTRY: Registry = Registry {
callsites: LinkedList::new(),
dispatchers: RwLock::new(Vec::new()),
};
}

fn rebuild_callsite_interest(
dispatchers: &mut MutexGuard<'_, Vec<dispatcher::Registrar>>,
callsite: &'static dyn Callsite,
) {
let meta = callsite.metadata();

// Iterate over the subscribers in the registry, and — if they are
// active — register the callsite with them.
let mut interests = dispatchers
.iter()
.filter_map(|registrar| registrar.try_register(meta));

// Use the first subscriber's `Interest` as the base value.
let interest = if let Some(interest) = interests.next() {
// Combine all remaining `Interest`s.
interests.fold(interest, Interest::and)
} else {
// If nobody was interested in this thing, just return `never`.
Interest::never()
};

callsite.set_interest(interest)
}
/// Clear and reregister interest on every [`Callsite`]
///
/// This function is intended for runtime reconfiguration of filters on traces
/// when the filter recalculation is much less frequent than trace events are.
/// The alternative is to have the [`Subscriber`] that supports runtime
/// reconfiguration of filters always return [`Interest::sometimes()`] so that
/// [`enabled`] is evaluated for every event.
///
/// This function will also re-compute the global maximum level as determined by
/// the [`max_level_hint`] method. If a [`Subscriber`]
/// implementation changes the value returned by its `max_level_hint`
/// implementation at runtime, then it **must** call this function after that
/// value changes, in order for the change to be reflected.
///
/// [`max_level_hint`]: crate::subscriber::Subscriber::max_level_hint
/// [`Callsite`]: crate::callsite::Callsite
/// [`enabled`]: crate::subscriber::Subscriber::enabled
/// [`Interest::sometimes()`]: crate::subscriber::Interest::sometimes
/// [`Subscriber`]: crate::subscriber::Subscriber
pub fn rebuild_interest_cache() {
let mut dispatchers = REGISTRY.dispatchers.write().unwrap();
let callsites = &REGISTRY.callsites;
rebuild_interest(callsites, &mut dispatchers);
}

fn rebuild_interest(
callsites: &Callsites,
dispatchers: &mut MutexGuard<'_, Vec<dispatcher::Registrar>>,
) {
let mut max_level = LevelFilter::OFF;
dispatchers.retain(|registrar| {
if let Some(dispatch) = registrar.upgrade() {
// If the subscriber did not provide a max level hint, assume
// that it may enable every level.
let level_hint = dispatch.max_level_hint().unwrap_or(LevelFilter::TRACE);
if level_hint > max_level {
max_level = level_hint;
}
true
/// Register a new `Callsite` with the global registry.
///
/// This should be called once per callsite after the callsite has been
/// constructed.
pub fn register(registration: &'static Registration) {
let dispatchers = REGISTRY.dispatchers.read().unwrap();
rebuild_callsite_interest(&dispatchers, registration.callsite);
REGISTRY.callsites.push(registration);
}

pub(crate) fn register_dispatch(dispatch: &Dispatch) {
let mut dispatchers = REGISTRY.dispatchers.write().unwrap();
let callsites = &REGISTRY.callsites;

dispatchers.push(dispatch.registrar());

rebuild_interest(callsites, &mut dispatchers);
}

fn rebuild_callsite_interest(
dispatchers: &[dispatcher::Registrar],
callsite: &'static dyn Callsite,
) {
let meta = callsite.metadata();

// Iterate over the subscribers in the registry, and — if they are
// active — register the callsite with them.
let mut interests = dispatchers.iter().filter_map(|registrar| {
registrar
.upgrade()
.map(|dispatch| dispatch.register_callsite(meta))
});

// Use the first subscriber's `Interest` as the base value.
let interest = if let Some(interest) = interests.next() {
// Combine all remaining `Interest`s.
interests.fold(interest, Interest::and)
} else {
false
}
});
// If nobody was interested in this thing, just return `never`.
Interest::never()
};

callsite.set_interest(interest)
}

fn rebuild_interest(callsites: &Callsites, dispatchers: &mut Vec<dispatcher::Registrar>) {
let mut max_level = LevelFilter::OFF;
dispatchers.retain(|registrar| {
if let Some(dispatch) = registrar.upgrade() {
// If the subscriber did not provide a max level hint, assume
// that it may enable every level.
let level_hint = dispatch.max_level_hint().unwrap_or(LevelFilter::TRACE);
if level_hint > max_level {
max_level = level_hint;
}
true
} else {
false
}
});

callsites.for_each(|reg| rebuild_callsite_interest(dispatchers, reg.callsite));

LevelFilter::set_max(max_level);
}
}

#[cfg(not(feature = "std"))]
mod inner {
use super::*;
static REGISTRY: Callsites = LinkedList::new();

/// Clear and reregister interest on every [`Callsite`]
///
/// This function is intended for runtime reconfiguration of filters on traces
/// when the filter recalculation is much less frequent than trace events are.
/// The alternative is to have the [`Subscriber`] that supports runtime
/// reconfiguration of filters always return [`Interest::sometimes()`] so that
/// [`enabled`] is evaluated for every event.
///
/// This function will also re-compute the global maximum level as determined by
/// the [`max_level_hint`] method. If a [`Subscriber`]
/// implementation changes the value returned by its `max_level_hint`
/// implementation at runtime, then it **must** call this function after that
/// value changes, in order for the change to be reflected.
///
/// [`max_level_hint`]: crate::subscriber::Subscriber::max_level_hint
/// [`Callsite`]: crate::callsite::Callsite
/// [`enabled`]: crate::subscriber::Subscriber::enabled
/// [`Interest::sometimes()`]: crate::subscriber::Interest::sometimes
/// [`Subscriber`]: crate::subscriber::Subscriber
pub fn rebuild_interest_cache() {
register_dispatch(dispatcher::get_global());
}

/// Register a new `Callsite` with the global registry.
///
/// This should be called once per callsite after the callsite has been
/// constructed.
pub fn register(registration: &'static Registration) {
rebuild_callsite_interest(dispatcher::get_global(), registration.callsite);
REGISTRY.push(registration);
}

pub(crate) fn register_dispatch(dispatcher: &Dispatch) {
// If the subscriber did not provide a max level hint, assume
// that it may enable every level.
let level_hint = dispatcher.max_level_hint().unwrap_or(LevelFilter::TRACE);

callsites.for_each(|reg| rebuild_callsite_interest(dispatchers, reg.callsite));
REGISTRY.for_each(|reg| rebuild_callsite_interest(dispatcher, reg.callsite));

LevelFilter::set_max(max_level);
LevelFilter::set_max(level_hint);
}

fn rebuild_callsite_interest(dispatcher: &Dispatch, callsite: &'static dyn Callsite) {
let meta = callsite.metadata();

callsite.set_interest(dispatcher.register_callsite(meta))
}
}

// ===== impl Identifier =====
Expand Down Expand Up @@ -220,17 +282,19 @@ impl fmt::Debug for Registration {
// ===== impl LinkedList =====

/// An intrusive atomic push-only linked list.
struct LinkedList {
head: AtomicPtr<Registration>,
struct LinkedList<T = &'static dyn Callsite> {
head: AtomicPtr<Registration<T>>,
}

impl LinkedList {
fn new() -> Self {
impl<T> LinkedList<T> {
const fn new() -> Self {
LinkedList {
head: AtomicPtr::new(ptr::null_mut()),
}
}
}

impl LinkedList {
fn for_each(&self, mut f: impl FnMut(&'static Registration)) {
let mut head = self.head.load(Ordering::Acquire);

Expand Down
Loading