maitake-sync 0.2.2

No-std async synchronization primitives from Maitake
Documentation
//! Reusable utilities for synchronization primitives.
//!
//! This module contains utility code used in the implementation of the
//! synchronization primitives provided by `maitake-sync`. To enable code reuse,
//! some of these utilities are exposed as public APIs in this module, so that
//! projects depending on `maitake-sync` can use them as well.
//!
//! This module exposes the following APIs:
//!
//! - [`Backoff`]: exponential backoff for spin loops
//! - [`CachePadded`]: pads and aligns a value to the size of a cache line

#[cfg(any(test, feature = "tracing", loom))]
macro_rules! trace {
    ($($t:tt)*) => { tracing::trace!($($t)*) }
}

#[cfg(not(any(test, feature = "tracing", loom)))]
macro_rules! trace {
    ($($t:tt)*) => {};
}

#[cfg(all(not(test), not(all(maitake_ultraverbose, feature = "tracing"))))]
macro_rules! test_dbg {
    ($e:expr) => {
        $e
    };
}

#[cfg(any(test, all(maitake_ultraverbose, feature = "tracing")))]
macro_rules! test_dbg {
    ($e:expr) => {
        match $e {
            e => {
                tracing::debug!(
                    location = %core::panic::Location::caller(),
                    "{} = {:?}",
                    stringify!($e),
                    &e
                );
                e
            }
        }
    };
}

#[cfg(all(not(test), not(all(maitake_ultraverbose, feature = "tracing"))))]
macro_rules! test_debug {
    ($($t:tt)*) => {};
}

#[cfg(any(test, all(maitake_ultraverbose, feature = "tracing")))]
macro_rules! test_debug {
    ($($t:tt)*) => { tracing::debug!($($t)*) }
}

#[cfg(all(not(test), not(all(maitake_ultraverbose, feature = "tracing"))))]
macro_rules! test_trace {
    ($($t:tt)*) => {};
}

#[cfg(any(test, all(maitake_ultraverbose, feature = "tracing")))]
macro_rules! test_trace {
    ($($t:tt)*) => { tracing::trace!($($t)*) }
}

#[cfg(all(not(test), not(all(maitake_ultraverbose, feature = "tracing"))))]
macro_rules! enter_test_debug_span {
    ($($args:tt)+) => {};
}

#[cfg(any(test, all(maitake_ultraverbose, feature = "tracing")))]
macro_rules! enter_test_debug_span {
    ($($args:tt)+) => {
        let _span = tracing::debug_span!($($args)+).entered();
    };
}

macro_rules! fmt_bits {
    ($self: expr, $f: expr, $has_states: ident, $($name: ident),+) => {
        $(
            if $self.contains(Self::$name) {
                if $has_states {
                    $f.write_str(" | ")?;
                }
                $f.write_str(stringify!($name))?;
                $has_states = true;
            }
        )+

    };
}

macro_rules! feature {
    (
        #![$meta:meta]
        $($item:item)*
    ) => {
        $(
            #[cfg($meta)]
            #[cfg_attr(docsrs, doc(cfg($meta)))]
            $item
        )*
    }
}

macro_rules! loom_const_fn {
    (
        $(#[$meta:meta])*
        $vis:vis unsafe fn $name:ident($($arg:ident: $T:ty),*) -> $Ret:ty $body:block
    ) => {
        $(#[$meta])*
        #[cfg(not(loom))]
        $vis const unsafe fn $name($($arg: $T),*) -> $Ret $body

        $(#[$meta])*
        #[cfg(loom)]
        $vis unsafe fn $name($($arg: $T),*) -> $Ret $body
    };
    (
        $(#[$meta:meta])*
        $vis:vis fn $name:ident($($arg:ident: $T:ty),*) -> $Ret:ty $body:block
    ) => {
        $(#[$meta])*
        #[cfg(not(loom))]
        $vis const fn $name($($arg: $T),*) -> $Ret $body

        $(#[$meta])*
        #[cfg(loom)]
        $vis fn $name($($arg: $T),*) -> $Ret $body
    }
}

/// Indicates unreachable code that we are confident is *truly* unreachable.
///
/// This is essentially a compromise between `core::unreachable!()` and
/// `core::hint::unreachable_unchecked()`. In debug mode builds and in tests,
/// this expands to `unreachable!()`, causing a panic. However, in release mode
/// non-test builds, this expands to `unreachable_unchecked`. Thus, this is a
/// somewhat safer form of `unreachable_unchecked` that will allow cases where
/// `unreachable_unchecked` would be invalid to be detected early.
///
/// Nonetheless, this must still be used with caution! If code is not adequately
/// tested, it is entirely possible for the `unreachable_unchecked` to be
/// reached in a scenario that was not reachable in tests.
macro_rules! unreachable_unchecked {
    () => ({
        #[cfg(any(test, debug_assertions))]
        panic!(
            concat!(
                env!("CARGO_PKG_NAME"),
                "internal error: entered unreachable code \n",
                "/!\\ EXTREMELY SERIOUS WARNING: in release mode, this would have been\n",
                "    `unreachable_unchecked`! This could result in undefine behavior.\n",
                "    Please double- or triple-check any assumptions about code which\n,",
                "    could lead to this being triggered."
            ),
        );
        #[allow(unreachable_code)] // lol
        {
            core::hint::unreachable_unchecked();
        }
    });
    ($msg:expr) => ({
        unreachable_unchecked!("{}", $msg)
    });
    ($msg:expr,) => ({
        unreachable_unchecked!($msg)
    });
    ($fmt:expr, $($arg:tt)*) => ({
        #[cfg(any(test, debug_assertions))]
        panic!(
            concat!(
                env!("CARGO_PKG_NAME"),
                "internal error: entered unreachable code: ",
                $fmt,
                "\n/!\\ EXTREMELY SERIOUS WARNING: in release mode, this would have been \n\
                \x32   `unreachable_unchecked`! This could result in undefine behavior. \n\
                \x32   Please double- or triple-check any assumptions about code which \n\
                \x32   could lead to this being triggered."
            ),
            $($arg)*
        );
        #[allow(unreachable_code)] // lol
        {
            core::hint::unreachable_unchecked();
        }
    });
}

mod backoff;
mod cache_pad;
pub(crate) mod fmt;
mod maybe_uninit;
mod wake_batch;

#[cfg(all(test, not(loom)))]
pub(crate) use self::test::trace_init;
pub use self::{backoff::Backoff, cache_pad::CachePadded};
pub(crate) use self::{maybe_uninit::CheckedMaybeUninit, wake_batch::WakeBatch};

#[cfg(test)]
pub(crate) mod test {
    /// A guard that represents the tracing default subscriber guard
    ///
    /// *should* be held until the end of the test, to ensure that tracing messages
    /// actually make it to the fmt subscriber for the entire test.
    ///
    /// Exists to abstract over tracing 01/02 guard type differences.
    #[must_use]
    #[cfg(all(test, not(loom)))]
    pub struct TestGuard {
        _x1: tracing::subscriber::DefaultGuard,
    }

    /// Initialize tracing with a default filter directive
    ///
    /// Returns a [TestGuard] that must be held for the duration of test to ensure
    /// tracing messages are correctly output
    #[cfg(all(test, not(loom)))]
    pub(crate) fn trace_init() -> TestGuard {
        trace_init_with_default("maitake=debug,cordyceps=debug")
    }

    /// Initialize tracing with the given filter directive
    ///
    /// Returns a [TestGuard] that must be held for the duration of test to ensure
    /// tracing messages are correctly output
    #[cfg(all(test, not(loom)))]
    pub(crate) fn trace_init_with_default(default: &str) -> TestGuard {
        use tracing_subscriber::{
            filter::{EnvFilter, LevelFilter},
            util::SubscriberInitExt,
        };
        const ENV: &str = if cfg!(loom) { "LOOM_LOG" } else { "RUST_LOG" };

        let env = std::env::var(ENV).unwrap_or_default();
        let builder = EnvFilter::builder().with_default_directive(LevelFilter::INFO.into());
        let filter = if env.is_empty() {
            builder
                .parse(default)
                .unwrap()
                // enable "loom=info" if using the default, so that we get
                // loom's thread number and iteration count traces.
                .add_directive("loom=info".parse().unwrap())
        } else {
            builder.parse_lossy(env)
        };
        let collector = tracing_subscriber::fmt()
            .with_env_filter(filter)
            .with_test_writer()
            .without_time()
            .finish();

        TestGuard {
            _x1: collector.set_default(),
        }
    }

    #[allow(dead_code)]
    pub(crate) fn assert_send<T: Send>() {}

    #[allow(dead_code)]
    pub(crate) fn assert_sync<T: Sync>() {}
    pub(crate) fn assert_send_sync<T: Send + Sync>() {}

    pub(crate) fn assert_future<F: core::future::Future>() {}

    pub(crate) struct NopRawMutex;

    unsafe impl mutex_traits::RawMutex for NopRawMutex {
        type GuardMarker = ();
        fn lock(&self) {
            unimplemented!("don't actually try to lock this thing")
        }

        fn is_locked(&self) -> bool {
            unimplemented!("don't actually try to lock this thing")
        }

        fn try_lock(&self) -> bool {
            unimplemented!("don't actually try to lock this thing")
        }

        unsafe fn unlock(&self) {
            unimplemented!("don't actually try to lock this thing")
        }
    }
}