maitake/
util.rs

1use core::ptr::NonNull;
2
3macro_rules! feature {
4    (
5        #![$meta:meta]
6        $($item:item)*
7    ) => {
8        $(
9            #[cfg($meta)]
10            #[cfg_attr(docsrs, doc(cfg($meta)))]
11            $item
12        )*
13    }
14}
15
16macro_rules! loom_const_fn {
17    (
18        $(#[$meta:meta])*
19        $vis:vis unsafe fn $name:ident($($arg:ident: $T:ty),*) -> $Ret:ty $body:block
20    ) => {
21        $(#[$meta])*
22        #[cfg(not(loom))]
23        $vis const unsafe fn $name($($arg: $T),*) -> $Ret $body
24
25        $(#[$meta])*
26        #[cfg(loom)]
27        $vis unsafe fn $name($($arg: $T),*) -> $Ret $body
28    };
29    (
30        $(#[$meta:meta])*
31        $vis:vis fn $name:ident($($arg:ident: $T:ty),*) -> $Ret:ty $body:block
32    ) => {
33        $(#[$meta])*
34        #[cfg(not(loom))]
35        $vis const fn $name($($arg: $T),*) -> $Ret $body
36
37        $(#[$meta])*
38        #[cfg(loom)]
39        $vis fn $name($($arg: $T),*) -> $Ret $body
40    }
41}
42
43/// Helper to construct a `NonNull<T>` from a raw pointer to `T`, with null
44/// checks elided in release mode.
45#[cfg(debug_assertions)]
46#[track_caller]
47#[inline(always)]
48pub(crate) unsafe fn non_null<T>(ptr: *mut T) -> NonNull<T> {
49    NonNull::new(ptr).expect(
50        "/!\\ constructed a `NonNull` from a null pointer! /!\\ \n\
51        in release mode, this would have called `NonNull::new_unchecked`, \
52        violating the `NonNull` invariant! this is a bug in `cordyceps!`.",
53    )
54}
55
56/// Helper to construct a `NonNull<T>` from a raw pointer to `T`, with null
57/// checks elided in release mode.
58///
59/// This is the release mode version.
60#[cfg(not(debug_assertions))]
61#[inline(always)]
62pub(crate) unsafe fn non_null<T>(ptr: *mut T) -> NonNull<T> {
63    NonNull::new_unchecked(ptr)
64}
65
66#[cfg(test)]
67pub(crate) use self::test::trace_init;
68
69pub(crate) fn expect_display<T, E: core::fmt::Display>(result: Result<T, E>, msg: &str) -> T {
70    match result {
71        Ok(t) => t,
72        Err(error) => panic!("{msg}: {error}"),
73    }
74}
75
76#[cfg(test)]
77pub(crate) mod test {
78    /// A guard that represents the tracing default subscriber guard
79    ///
80    /// *should* be held until the end of the test, to ensure that tracing messages
81    /// actually make it to the fmt subscriber for the entire test.
82    ///
83    /// Exists to abstract over tracing 01/02 guard type differences.
84    #[must_use]
85    pub struct TestGuard {
86        #[cfg(not(loom))]
87        _x2: tracing_02::collect::DefaultGuard,
88        #[cfg(loom)]
89        _x1: tracing_01::subscriber::DefaultGuard,
90    }
91
92    /// Initialize tracing with a default filter directive
93    ///
94    /// Returns a [TestGuard] that must be held for the duration of test to ensure
95    /// tracing messages are correctly output
96    pub(crate) fn trace_init() -> TestGuard {
97        trace_init_with_default("maitake=debug,cordyceps=debug")
98    }
99
100    /// Initialize tracing with the given filter directive
101    ///
102    /// Returns a [TestGuard] that must be held for the duration of test to ensure
103    /// tracing messages are correctly output
104    #[cfg(not(loom))]
105    pub(crate) fn trace_init_with_default(default: &str) -> TestGuard {
106        use tracing_subscriber::filter::{EnvFilter, LevelFilter};
107        let env = std::env::var("RUST_LOG").unwrap_or_default();
108        let builder = EnvFilter::builder().with_default_directive(LevelFilter::INFO.into());
109        let filter = if env.is_empty() {
110            builder.parse(default).unwrap()
111        } else {
112            builder.parse_lossy(env)
113        };
114        // enable traces from alloc leak checking.
115        let filter = filter.add_directive("maitake::alloc=trace".parse().unwrap());
116        let collector = tracing_subscriber::fmt()
117            .with_env_filter(filter)
118            .with_test_writer()
119            .without_time()
120            .finish();
121
122        TestGuard {
123            _x2: tracing_02::collect::set_default(collector),
124        }
125    }
126
127    /// Initialize tracing with the given filter directive
128    ///
129    /// Returns a [TestGuard] that must be held for the duration of test to ensure
130    /// tracing messages are correctly output
131    #[cfg(loom)]
132    pub(crate) fn trace_init_with_default(default: &str) -> TestGuard {
133        use tracing_subscriber_03::filter::{EnvFilter, LevelFilter};
134        let env = std::env::var("LOOM_LOG").unwrap_or_default();
135        let builder = EnvFilter::builder().with_default_directive(LevelFilter::INFO.into());
136        let filter = if env.is_empty() {
137            builder
138                .parse(default)
139                .unwrap()
140                // enable "loom=info" if using the default, so that we get
141                // loom's thread number and iteration count traces.
142                .add_directive("loom=info".parse().unwrap())
143        } else {
144            builder.parse_lossy(env)
145        };
146        let collector = tracing_subscriber_03::fmt()
147            .with_env_filter(filter)
148            .with_test_writer()
149            .without_time()
150            .finish();
151
152        use tracing_subscriber_03::util::SubscriberInitExt;
153        TestGuard {
154            _x1: collector.set_default(),
155        }
156    }
157}