diff --git a/crates/bevy_utils/Cargo.toml b/crates/bevy_utils/Cargo.toml index 01751764b4e00..2719b4372f90b 100644 --- a/crates/bevy_utils/Cargo.toml +++ b/crates/bevy_utils/Cargo.toml @@ -10,12 +10,39 @@ keywords = ["bevy"] [features] default = ["std", "serde", "tracing"] -std = ["alloc", "tracing?/std", "foldhash/std", "dep:thread_local"] -alloc = ["hashbrown"] -detailed_trace = [] + +# Functionality + +## Adds serialization support through `serde`. serde = ["hashbrown/serde"] + +# Debugging Features + +## Enables `tracing` integration, allowing spans and other metrics to be reported +## through that framework. tracing = ["dep:tracing"] +## Enables more detailed reporting via `tracing`. +detailed_trace = ["tracing"] + +# Platform Compatibility + +## Allows access to the `std` crate. Enabling this feature will prevent compilation +## on `no_std` targets, but provides access to certain additional features on +## supported platforms. +std = ["alloc", "tracing?/std", "foldhash/std", "dep:thread_local"] + +## Allows access to the `alloc` crate. +alloc = ["hashbrown"] + +## `critical-section` provides the building blocks for synchronization primitives +## on all platforms, including `no_std`. +critical-section = ["portable-atomic?/critical-section"] + +## `portable-atomic` provides additional platform support for atomic types and +## operations, even on targets without native support. +portable-atomic = ["dep:portable-atomic"] + [dependencies] foldhash = { version = "0.1.3", default-features = false } tracing = { version = "0.1", default-features = false, optional = true } @@ -24,6 +51,9 @@ hashbrown = { version = "0.15.1", features = [ "raw-entry", ], optional = true, default-features = false } thread_local = { version = "1.0", optional = true } +portable-atomic = { version = "1", default-features = false, features = [ + "fallback", +], optional = true } [dev-dependencies] static_assertions = "1.1.0" diff --git a/crates/bevy_utils/src/lib.rs b/crates/bevy_utils/src/lib.rs index 7b660ed70e6e3..fea65f11ed686 100644 --- a/crates/bevy_utils/src/lib.rs +++ b/crates/bevy_utils/src/lib.rs @@ -34,6 +34,9 @@ mod once; mod parallel_queue; mod time; +#[doc(hidden)] +pub use once::OnceFlag; + /// For when you want a deterministic hasher. /// /// Seed was randomly generated with a fair dice roll. Guaranteed to be random: diff --git a/crates/bevy_utils/src/once.rs b/crates/bevy_utils/src/once.rs index 68aeb745559da..23ca669257790 100644 --- a/crates/bevy_utils/src/once.rs +++ b/crates/bevy_utils/src/once.rs @@ -1,11 +1,38 @@ +#[cfg(feature = "portable-atomic")] +use portable_atomic::{AtomicBool, Ordering}; + +#[cfg(not(feature = "portable-atomic"))] +use core::sync::atomic::{AtomicBool, Ordering}; + +/// Wrapper around an [`AtomicBool`], abstracting the backing implementation and +/// ordering considerations. +#[doc(hidden)] +pub struct OnceFlag(AtomicBool); + +impl OnceFlag { + /// Create a new flag in the unset state. + pub const fn new() -> Self { + Self(AtomicBool::new(true)) + } + + /// Sets this flag. Will return `true` if this flag hasn't been set before. + pub fn set(&self) -> bool { + self.0.swap(false, Ordering::Relaxed) + } +} + +impl Default for OnceFlag { + fn default() -> Self { + Self::new() + } +} + /// Call some expression only once per call site. #[macro_export] macro_rules! once { ($expression:expr) => {{ - use ::core::sync::atomic::{AtomicBool, Ordering}; - - static SHOULD_FIRE: AtomicBool = AtomicBool::new(true); - if SHOULD_FIRE.swap(false, Ordering::Relaxed) { + static SHOULD_FIRE: $crate::OnceFlag = $crate::OnceFlag::new(); + if SHOULD_FIRE.set() { $expression; } }};