Skip to content

Commit 2572f68

Browse files
authored
core: support no-std + alloc (#256)
* core: support `no-std` + `alloc` Motivation Users have expressed interest in using `tracing` on no_std platforms with `liballoc`. Solution This branch adds `no_std` support to `tracing-core` by adding a `std` feature flag (on by default) which can be disabled to use `libcore` + `liballoc` instead of `libstd`. When the `std` feature is disabled, `tracing-core` will use `spin::Mutex` rather than `std::sync::Mutex`, and the thread-local scoped dispatcher will be disabled (since it necessitates a defined OS threading model, which may not exist on `no_std` platforms). Refs: #213 Signed-off-by: Eliza Weisman <[email protected]>
1 parent 6bf48d0 commit 2572f68

12 files changed

+220
-28
lines changed

azure-pipelines.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ stages:
3434
displayName: "Test log support"
3535
- bash: cd tracing/test_static_max_level_features && cargo test
3636
displayName: "Test static max level features"
37+
- bash: cd tracing-core && cargo test --no-default-features
38+
displayName: "Test tracing-core no-std support"
3739
- job: custom_nightly
3840
pool:
3941
vmImage: ubuntu-16.04

tracing-core/Cargo.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,18 @@ categories = [
2626
keywords = ["logging", "tracing", "profiling"]
2727
edition = "2018"
2828

29+
[features]
30+
default = ["std"]
31+
std = []
32+
2933
[badges]
3034
azure-devops = { project = "tracing/tracing", pipeline = "tokio-rs.tracing", build = "1" }
3135
maintenance = { status = "actively-developed" }
3236

3337
[dependencies]
3438
lazy_static = "1"
3539

40+
[target.'cfg(not(feature = "std"))'.dependencies]
41+
spin = "0.5"
42+
lazy_static = { version = "1", features = ["spin_no_std"] }
43+

tracing-core/README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,33 @@ The crate provides:
4646
In addition, it defines the global callsite registry and per-thread current
4747
dispatcher which other components of the tracing system rely on.
4848

49+
## Usage
50+
4951
Application authors will typically not use this crate directly. Instead, they
5052
will use the [`tracing`] crate, which provides a much more fully-featured
5153
API. However, this crate's API will change very infrequently, so it may be used
5254
when dependencies must be very stable.
5355

56+
`Subscriber` implementations may depend on `tracing-core` rather than `tracing`,
57+
as the additional APIs provided by `tracing` are primarily useful for
58+
instrumenting libraries and applications, and are generally not necessary for
59+
`Subscriber` implementations.
60+
61+
### Crate Feature Flags
62+
63+
The following crate feature flags are available:
64+
65+
* `std`: Depend on the Rust standard library (enabled by default).
66+
67+
`no_std` users may disable this feature with `default-features = false`:
68+
69+
```toml
70+
[dependencies]
71+
tracing-core = { version = "0.1.4", default-features = false }
72+
```
73+
74+
**Note**:`tracing-core`'s `no_std` support requires `liballoc`.
75+
5476
[`tracing`]: ../tracing
5577
[`Span`]: https://docs.rs/tracing-core/0.1.3/tracing_core/span/struct.Span.html
5678
[`Event`]: https://docs.rs/tracing-core/0.1.3/tracing_core/event/struct.Event.html

tracing-core/src/callsite.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
//! Callsites represent the source locations from which spans or events
22
//! originate.
3-
use crate::{
4-
dispatcher::{self, Dispatch, Registrar},
5-
subscriber::Interest,
6-
Metadata,
7-
};
8-
use std::{
3+
use crate::stdlib::{
94
fmt,
105
hash::{Hash, Hasher},
116
ptr,
127
sync::Mutex,
8+
vec::Vec,
9+
};
10+
use crate::{
11+
dispatcher::{self, Dispatch, Registrar},
12+
subscriber::Interest,
13+
Metadata,
1314
};
1415

1516
lazy_static! {

tracing-core/src/dispatcher.rs

Lines changed: 65 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
//! # let my_dispatch = dispatcher::Dispatch::new(my_subscriber);
6969
//! // no default subscriber
7070
//!
71+
//! # #[cfg(feature = "std")]
7172
//! dispatcher::with_default(&my_dispatch, || {
7273
//! // my_subscriber is the default
7374
//! });
@@ -113,6 +114,10 @@
113114
//! # }
114115
//! ```
115116
//!
117+
//! **Note**: the thread-local scoped dispatcher (`with_default`) requires the
118+
//! Rust standard library. `no_std` users should use [`set_global_default`]
119+
//! instead.
120+
//!
116121
//! Finally, `tokio` users should note that versions of `tokio` >= 0.1.22
117122
//! support an `experimental-tracing` feature flag. When this flag is enabled,
118123
//! the `tokio` runtime's thread pool will automatically propagate the default
@@ -138,16 +143,21 @@ use crate::{
138143
Event, Metadata,
139144
};
140145

141-
use std::{
146+
use crate::stdlib::{
142147
any::Any,
143-
cell::{Cell, RefCell},
144-
error, fmt,
148+
fmt,
145149
sync::{
146150
atomic::{AtomicUsize, Ordering},
147151
Arc, Weak,
148152
},
149153
};
150154

155+
#[cfg(feature = "std")]
156+
use crate::stdlib::{
157+
cell::{Cell, RefCell},
158+
error,
159+
};
160+
151161
/// `Dispatch` trace data to a [`Subscriber`].
152162
///
153163
/// [`Subscriber`]: ../subscriber/trait.Subscriber.html
@@ -156,6 +166,7 @@ pub struct Dispatch {
156166
subscriber: Arc<dyn Subscriber + Send + Sync>,
157167
}
158168

169+
#[cfg(feature = "std")]
159170
thread_local! {
160171
static CURRENT_STATE: State = State {
161172
default: RefCell::new(Dispatch::none()),
@@ -171,6 +182,7 @@ const INITIALIZED: usize = 2;
171182
static mut GLOBAL_DISPATCH: Option<Dispatch> = None;
172183

173184
/// The dispatch state of a thread.
185+
#[cfg(feature = "std")]
174186
struct State {
175187
/// This thread's current default dispatcher.
176188
default: RefCell<Dispatch>,
@@ -186,16 +198,22 @@ struct State {
186198

187199
/// A guard that resets the current default dispatcher to the prior
188200
/// default dispatcher when dropped.
201+
#[cfg(feature = "std")]
189202
struct ResetGuard(Option<Dispatch>);
190203

191204
/// Sets this dispatch as the default for the duration of a closure.
192205
///
193206
/// The default dispatcher is used when creating a new [span] or
194207
/// [`Event`].
195208
///
209+
/// **Note**: This function requires the Rust standard library. `no_std` users
210+
/// should use [`set_global_default`] instead.
211+
///
196212
/// [span]: ../span/index.html
197213
/// [`Subscriber`]: ../subscriber/trait.Subscriber.html
198214
/// [`Event`]: ../event/struct.Event.html
215+
/// [`set_global_default`]: ../fn.set_global_default.html
216+
#[cfg(feature = "std")]
199217
pub fn with_default<T>(dispatcher: &Dispatch, f: impl FnOnce() -> T) -> T {
200218
// When this guard is dropped, the default dispatcher will be reset to the
201219
// prior default. Using this (rather than simply resetting after calling
@@ -243,6 +261,7 @@ impl fmt::Display for SetGlobalDefaultError {
243261
}
244262
}
245263

264+
#[cfg(feature = "std")]
246265
impl error::Error for SetGlobalDefaultError {}
247266

248267
/// Executes a closure with a reference to this thread's current [dispatcher].
@@ -252,6 +271,7 @@ impl error::Error for SetGlobalDefaultError {}
252271
/// with `Dispatch::none` rather than the previously set dispatcher.
253272
///
254273
/// [dispatcher]: ../dispatcher/struct.Dispatch.html
274+
#[cfg(feature = "std")]
255275
pub fn get_default<T, F>(mut f: F) -> T
256276
where
257277
F: FnMut(&Dispatch) -> T,
@@ -274,14 +294,10 @@ where
274294

275295
let mut default = state.default.borrow_mut();
276296

277-
if default.is::<NoSubscriber>() && GLOBAL_INIT.load(Ordering::SeqCst) == INITIALIZED
278-
{
279-
// don't redo this call on the next check
280-
unsafe {
281-
*default = GLOBAL_DISPATCH
282-
.as_ref()
283-
.expect("invariant violated: GLOBAL_DISPATCH must be initialized before GLOBAL_INIT is set")
284-
.clone()
297+
if default.is::<NoSubscriber>() {
298+
if let Some(global) = get_global() {
299+
// don't redo this call on the next check
300+
*default = global.clone();
285301
}
286302
}
287303
f(&*default)
@@ -292,6 +308,34 @@ where
292308
.unwrap_or_else(|_| f(&Dispatch::none()))
293309
}
294310

311+
/// Executes a closure with a reference to the current [dispatcher].
312+
///
313+
/// [dispatcher]: ../dispatcher/struct.Dispatch.html
314+
#[cfg(not(feature = "std"))]
315+
pub fn get_default<T, F>(mut f: F) -> T
316+
where
317+
F: FnMut(&Dispatch) -> T,
318+
{
319+
if let Some(d) = get_global() {
320+
f(d)
321+
} else {
322+
f(&Dispatch::none())
323+
}
324+
}
325+
326+
fn get_global() -> Option<&'static Dispatch> {
327+
if GLOBAL_INIT.load(Ordering::SeqCst) != INITIALIZED {
328+
return None;
329+
}
330+
unsafe {
331+
// This is safe given the invariant that setting the global dispatcher
332+
// also sets `GLOBAL_INIT` to `INITIALIZED`.
333+
Some(GLOBAL_DISPATCH.as_ref().expect(
334+
"invariant violated: GLOBAL_DISPATCH must be initialized before GLOBAL_INIT is set",
335+
))
336+
}
337+
}
338+
295339
pub(crate) struct Registrar(Weak<dyn Subscriber + Send + Sync>);
296340

297341
impl Dispatch {
@@ -568,6 +612,7 @@ impl Registrar {
568612

569613
// ===== impl State =====
570614

615+
#[cfg(feature = "std")]
571616
impl State {
572617
/// Replaces the current default dispatcher on this thread with the provided
573618
/// dispatcher.Any
@@ -588,6 +633,7 @@ impl State {
588633

589634
// ===== impl ResetGuard =====
590635

636+
#[cfg(feature = "std")]
591637
impl Drop for ResetGuard {
592638
#[inline]
593639
fn drop(&mut self) {
@@ -602,14 +648,15 @@ impl Drop for ResetGuard {
602648
#[cfg(test)]
603649
mod test {
604650
use super::*;
651+
#[cfg(feature = "std")]
652+
use crate::stdlib::sync::atomic::{AtomicUsize, Ordering};
605653
use crate::{
606654
callsite::Callsite,
607655
metadata::{Kind, Level, Metadata},
608656
span,
609657
subscriber::{Interest, Subscriber},
610658
Event,
611659
};
612-
use std::sync::atomic::{AtomicUsize, Ordering};
613660

614661
#[test]
615662
fn dispatch_is() {
@@ -642,6 +689,7 @@ mod test {
642689
}
643690

644691
#[test]
692+
#[cfg(feature = "std")]
645693
fn events_dont_infinite_loop() {
646694
// This test ensures that an event triggered within a subscriber
647695
// won't cause an infinite loop of events.
@@ -680,6 +728,7 @@ mod test {
680728
}
681729

682730
#[test]
731+
#[cfg(feature = "std")]
683732
fn spans_dont_infinite_loop() {
684733
// This test ensures that a span created within a subscriber
685734
// won't cause an infinite loop of new spans.
@@ -740,7 +789,10 @@ mod test {
740789
fn enter(&self, _: &span::Id) {}
741790
fn exit(&self, _: &span::Id) {}
742791
}
792+
#[cfg(feature = "std")]
743793
struct TestSubscriberB;
794+
795+
#[cfg(feature = "std")]
744796
impl Subscriber for TestSubscriberB {
745797
fn enabled(&self, _: &Metadata<'_>) -> bool {
746798
true
@@ -765,6 +817,7 @@ mod test {
765817
)
766818
});
767819

820+
#[cfg(feature = "std")]
768821
with_default(&Dispatch::new(TestSubscriberB), || {
769822
get_default(|current| {
770823
assert!(

tracing-core/src/field.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
//! [`event`]: ../subscriber/trait.Subscriber.html#method.record
3939
//! [`Visit`]: trait.Visit.html
4040
use crate::callsite;
41-
use std::{
41+
use crate::stdlib::{
4242
borrow::Borrow,
4343
fmt,
4444
hash::{Hash, Hasher},
@@ -670,6 +670,7 @@ impl_valid_len! {
670670
mod test {
671671
use super::*;
672672
use crate::metadata::{Kind, Level, Metadata};
673+
use crate::stdlib::{borrow::ToOwned, string::String};
673674

674675
struct TestCallsite1;
675676
static TEST_CALLSITE_1: TestCallsite1 = TestCallsite1;
@@ -770,7 +771,7 @@ mod test {
770771

771772
struct MyVisitor;
772773
impl Visit for MyVisitor {
773-
fn record_debug(&mut self, field: &Field, _: &dyn (::std::fmt::Debug)) {
774+
fn record_debug(&mut self, field: &Field, _: &dyn (crate::stdlib::fmt::Debug)) {
774775
assert_eq!(field.callsite(), TEST_META_1.callsite())
775776
}
776777
}
@@ -789,7 +790,7 @@ mod test {
789790
let valueset = fields.value_set(values);
790791
let mut result = String::new();
791792
valueset.record(&mut |_: &Field, value: &dyn fmt::Debug| {
792-
use std::fmt::Write;
793+
use crate::stdlib::fmt::Write;
793794
write!(&mut result, "{:?}", value).unwrap();
794795
});
795796
assert_eq!(result, "123".to_owned());

tracing-core/src/lib.rs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,36 @@
2626
//! In addition, it defines the global callsite registry and per-thread current
2727
//! dispatcher which other components of the tracing system rely on.
2828
//!
29+
//! ## Usage
30+
//!
2931
//! Application authors will typically not use this crate directly. Instead,
30-
//! they will use the `tracing` crate, which provides a much more
32+
//! they will use the [`tracing`] crate, which provides a much more
3133
//! fully-featured API. However, this crate's API will change very infrequently,
3234
//! so it may be used when dependencies must be very stable.
3335
//!
36+
//! `Subscriber` implementations may depend on `tracing-core` rather than
37+
//! `tracing`, as the additional APIs provided by `tracing` are primarily useful
38+
//! for instrumenting libraries and applications, and are generally not
39+
//! necessary for `Subscriber` implementations.
40+
//!
3441
//! The [`tokio-rs/tracing`] repository contains less stable crates designed to
3542
//! be used with the `tracing` ecosystem. It includes a collection of
3643
//! `Subscriber` implementations, as well as utility and adapter crates.
3744
//!
45+
//! ### Crate Feature Flags
46+
//!
47+
//! The following crate feature flags are available:
48+
//!
49+
//! * `std`: Depend on the Rust standard library (enabled by default).
50+
//!
51+
//! `no_std` users may disable this feature with `default-features = false`:
52+
//!
53+
//! ```toml
54+
//! [dependencies]
55+
//! tracing-core = { version = "0.1.4", default-features = false }
56+
//! ```
57+
//! **Note**:`tracing-core`'s `no_std` support requires `liballoc`.
58+
//!
3859
//! [`Span`]: span/struct.Span.html
3960
//! [`Event`]: event/struct.Event.html
4061
//! [`Subscriber`]: subscriber/trait.Subscriber.html
@@ -47,6 +68,11 @@
4768
//! [`Dispatch`]: dispatcher/struct.Dispatch.html
4869
//! [`tokio-rs/tracing`]: https://github.com/tokio-rs/tracing
4970
//! [`tracing`]: https://crates.io/crates/tracing
71+
#![cfg_attr(not(feature = "std"), no_std)]
72+
73+
#[cfg(not(feature = "std"))]
74+
extern crate alloc;
75+
5076
#[macro_use]
5177
extern crate lazy_static;
5278

@@ -192,6 +218,7 @@ pub mod field;
192218
pub mod metadata;
193219
mod parent;
194220
pub mod span;
221+
pub(crate) mod stdlib;
195222
pub mod subscriber;
196223

197224
pub use self::{

0 commit comments

Comments
 (0)