Skip to content

Commit

Permalink
Automatically enable portable-atomic when required (#17570)
Browse files Browse the repository at this point in the history
# Objective

- Contributes to #15460
- Reduce quantity and complexity of feature gates across Bevy

## Solution

- Used `target_has_atomic` configuration variable to automatically
detect impartial atomic support and automatically switch to
`portable-atomic` over the standard library on an as-required basis.

## Testing

- CI

## Notes

To explain the technique employed here, consider getting `Arc` either
from `alloc::sync` _or_ `portable-atomic-util`. First, we can inspect
the `alloc` crate to see that you only have access to `Arc` _if_
`target_has_atomic = "ptr"`. We add a target dependency for this
particular configuration _inverted_:

```toml
[target.'cfg(not(target_has_atomic = "ptr"))'.dependencies]
portable-atomic-util = { version = "0.2.4", default-features = false }
```

This ensures we only have the dependency when it is needed, and it is
entirely excluded from the dependency graph when it is not. Next, we
adjust our configuration flags to instead of checking for `feature =
"portable-atomic"` to instead check for `target_has_atomic = "ptr"`:

```rust
// `alloc` feature flag hidden for brevity

#[cfg(not(target_has_atomic = "ptr"))]
use portable_atomic_util as arc;

#[cfg(target_has_atomic = "ptr")]
use alloc::sync as arc;

pub use arc::{Arc, Weak};
```

The benefits of this technique are three-fold:

1. For platforms without full atomic support, the functionality is
enabled automatically.
2. For platforms with atomic support, the dependency is never included,
even if a feature was enabled using `--all-features` (for example)
3. The `portable-atomic` feature no longer needs to virally spread to
all user-facing crates, it's instead something handled within
`bevy_platform_support` (with some extras where other dependencies also
need their features enabled).
  • Loading branch information
bushrat011899 authored Feb 24, 2025
1 parent 7f14581 commit 76e9bf9
Show file tree
Hide file tree
Showing 21 changed files with 94 additions and 152 deletions.
9 changes: 0 additions & 9 deletions crates/bevy_a11y/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,6 @@ critical-section = [
"bevy_input_focus/critical-section",
]

## `portable-atomic` provides additional platform support for atomic types and
## operations, even on targets without native support.
portable-atomic = [
"bevy_app/portable-atomic",
"bevy_ecs/portable-atomic",
"bevy_reflect?/portable-atomic",
"bevy_input_focus/portable-atomic",
]

## Uses the `libm` maths library instead of the one provided in `std` and `core`.
libm = ["bevy_input_focus/libm"]

Expand Down
9 changes: 0 additions & 9 deletions crates/bevy_app/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,6 @@ critical-section = [
"bevy_reflect?/critical-section",
]

## `portable-atomic` provides additional platform support for atomic types and
## operations, even on targets without native support.
portable-atomic = [
"bevy_tasks?/portable-atomic",
"bevy_ecs/portable-atomic",
"bevy_platform_support/portable-atomic",
"bevy_reflect?/portable-atomic",
]

[dependencies]
# bevy
bevy_derive = { path = "../bevy_derive", version = "0.16.0-dev" }
Expand Down
8 changes: 0 additions & 8 deletions crates/bevy_app/src/task_pool_plugin.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,3 @@
#![cfg_attr(
feature = "portable-atomic",
expect(
clippy::redundant_closure,
reason = "bevy_platform_support::sync::Arc has subtly different implicit behavior"
)
)]

use crate::{App, Plugin};

use alloc::string::ToString;
Expand Down
11 changes: 0 additions & 11 deletions crates/bevy_diagnostic/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,6 @@ critical-section = [
"bevy_tasks?/critical-section",
]

## `portable-atomic` provides additional platform support for atomic types and
## operations, even on targets without native support.
portable-atomic = [
"bevy_ecs/portable-atomic",
"bevy_app/portable-atomic",
"bevy_platform_support/portable-atomic",
"bevy_time/portable-atomic",
"bevy_utils/portable-atomic",
"bevy_tasks?/portable-atomic",
]

[dependencies]
# bevy
bevy_app = { path = "../bevy_app", version = "0.16.0-dev", default-features = false }
Expand Down
14 changes: 5 additions & 9 deletions crates/bevy_ecs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -92,15 +92,6 @@ critical-section = [
"bevy_reflect?/critical-section",
]

## `portable-atomic` provides additional platform support for atomic types and
## operations, even on targets without native support.
portable-atomic = [
"bevy_tasks?/portable-atomic",
"bevy_platform_support/portable-atomic",
"concurrent-queue/portable-atomic",
"bevy_reflect?/portable-atomic",
]

[dependencies]
bevy_ptr = { path = "../bevy_ptr", version = "0.16.0-dev" }
bevy_reflect = { path = "../bevy_reflect", version = "0.16.0-dev", features = [
Expand Down Expand Up @@ -139,6 +130,11 @@ tracing = { version = "0.1", default-features = false, optional = true }
log = { version = "0.4", default-features = false }
bumpalo = "3"

[target.'cfg(not(all(target_has_atomic = "8", target_has_atomic = "16", target_has_atomic = "32", target_has_atomic = "64", target_has_atomic = "ptr")))'.dependencies]
concurrent-queue = { version = "2.5.0", default-features = false, features = [
"portable-atomic",
] }

[dev-dependencies]
rand = "0.8"
static_assertions = "1.1.0"
Expand Down
6 changes: 3 additions & 3 deletions crates/bevy_ecs/src/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2060,7 +2060,7 @@ impl RequiredComponents {
//
// This would be resolved by https://github.com/rust-lang/rust/issues/123430

#[cfg(feature = "portable-atomic")]
#[cfg(not(target_has_atomic = "ptr"))]
use alloc::boxed::Box;

type Constructor = dyn for<'a, 'b> Fn(
Expand All @@ -2072,10 +2072,10 @@ impl RequiredComponents {
MaybeLocation,
);

#[cfg(feature = "portable-atomic")]
#[cfg(not(target_has_atomic = "ptr"))]
type Intermediate<T> = Box<T>;

#[cfg(not(feature = "portable-atomic"))]
#[cfg(target_has_atomic = "ptr")]
type Intermediate<T> = Arc<T>;

let boxed: Intermediate<Constructor> = Intermediate::new(
Expand Down
9 changes: 0 additions & 9 deletions crates/bevy_input/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,6 @@ critical-section = [
"bevy_platform_support/critical-section",
]

## `portable-atomic` provides additional platform support for atomic types and
## operations, even on targets without native support.
portable-atomic = [
"bevy_app/portable-atomic",
"bevy_ecs/portable-atomic",
"bevy_reflect?/portable-atomic",
"bevy_platform_support/portable-atomic",
]

## Uses the `libm` maths library instead of the one provided in `std` and `core`.
libm = ["bevy_math/libm"]

Expand Down
9 changes: 0 additions & 9 deletions crates/bevy_input_focus/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,6 @@ critical-section = [
"bevy_input/critical-section",
]

## `portable-atomic` provides additional platform support for atomic types and
## operations, even on targets without native support.
portable-atomic = [
"bevy_app/portable-atomic",
"bevy_ecs/portable-atomic",
"bevy_reflect?/portable-atomic",
"bevy_input/portable-atomic",
]

## Uses the `libm` maths library instead of the one provided in `std` and `core`.
libm = ["bevy_math/libm", "bevy_window/libm"]

Expand Down
31 changes: 15 additions & 16 deletions crates/bevy_platform_support/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,32 +24,20 @@ serialize = ["hashbrown/serde"]
std = [
"alloc",
"critical-section?/std",
"portable-atomic?/std",
"portable-atomic-util?/std",
"portable-atomic/std",
"portable-atomic-util/std",
"spin/std",
"foldhash/std",
]

alloc = ["portable-atomic-util?/alloc", "dep:hashbrown"]
alloc = ["portable-atomic-util/alloc", "dep:hashbrown"]

## `critical-section` provides the building blocks for synchronization primitives
## on all platforms, including `no_std`.
critical-section = ["dep: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",
"dep:portable-atomic-util",
"spin/portable_atomic",
]
critical-section = ["dep:critical-section", "portable-atomic/critical-section"]

[dependencies]
critical-section = { version = "1.2.0", default-features = false, optional = true }
portable-atomic = { version = "1", default-features = false, features = [
"fallback",
], optional = true }
portable-atomic-util = { version = "0.2.4", default-features = false, optional = true }
spin = { version = "0.9.8", default-features = false, features = [
"mutex",
"spin_mutex",
Expand All @@ -68,6 +56,17 @@ hashbrown = { version = "0.15.1", features = [
web-time = { version = "1.1", default-features = false }
getrandom = { version = "0.2.0", default-features = false, features = ["js"] }

[target.'cfg(not(all(target_has_atomic = "8", target_has_atomic = "16", target_has_atomic = "32", target_has_atomic = "64", target_has_atomic = "ptr")))'.dependencies]
portable-atomic = { version = "1", default-features = false, features = [
"fallback",
] }
spin = { version = "0.9.8", default-features = false, features = [
"portable_atomic",
] }

[target.'cfg(not(target_has_atomic = "ptr"))'.dependencies]
portable-atomic-util = { version = "0.2.4", default-features = false }

[lints]
workspace = true

Expand Down
42 changes: 34 additions & 8 deletions crates/bevy_platform_support/src/sync/atomic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,39 @@
//! Using these types will ensure the correct atomic provider is used without the need for
//! feature gates in your own code.
pub use atomic::{
AtomicBool, AtomicI16, AtomicI32, AtomicI64, AtomicI8, AtomicIsize, AtomicPtr, AtomicU16,
AtomicU32, AtomicU64, AtomicU8, AtomicUsize, Ordering,
};
pub use atomic_16::{AtomicI16, AtomicU16};
pub use atomic_32::{AtomicI32, AtomicU32};
pub use atomic_64::{AtomicI64, AtomicU64};
pub use atomic_8::{AtomicBool, AtomicI8, AtomicU8};
pub use atomic_ptr::{AtomicIsize, AtomicPtr, AtomicUsize};
pub use core::sync::atomic::Ordering;

#[cfg(not(feature = "portable-atomic"))]
use core::sync::atomic;
#[cfg(target_has_atomic = "8")]
use core::sync::atomic as atomic_8;

#[cfg(feature = "portable-atomic")]
use portable_atomic as atomic;
#[cfg(not(target_has_atomic = "8"))]
use portable_atomic as atomic_8;

#[cfg(target_has_atomic = "16")]
use core::sync::atomic as atomic_16;

#[cfg(not(target_has_atomic = "16"))]
use portable_atomic as atomic_16;

#[cfg(target_has_atomic = "32")]
use core::sync::atomic as atomic_32;

#[cfg(not(target_has_atomic = "32"))]
use portable_atomic as atomic_32;

#[cfg(target_has_atomic = "64")]
use core::sync::atomic as atomic_64;

#[cfg(not(target_has_atomic = "64"))]
use portable_atomic as atomic_64;

#[cfg(target_has_atomic = "ptr")]
use core::sync::atomic as atomic_ptr;

#[cfg(not(target_has_atomic = "ptr"))]
use portable_atomic as atomic_ptr;
4 changes: 2 additions & 2 deletions crates/bevy_platform_support/src/sync/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ mod once;
mod poison;
mod rwlock;

#[cfg(all(feature = "alloc", feature = "portable-atomic"))]
#[cfg(all(feature = "alloc", not(target_has_atomic = "ptr")))]
use portable_atomic_util as arc;

#[cfg(all(feature = "alloc", not(feature = "portable-atomic")))]
#[cfg(all(feature = "alloc", target_has_atomic = "ptr"))]
use alloc::sync as arc;
7 changes: 0 additions & 7 deletions crates/bevy_reflect/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,6 @@ critical-section = [
"bevy_utils/critical-section",
]

## `portable-atomic` provides additional platform support for atomic types and
## operations, even on targets without native support.
portable-atomic = [
"bevy_platform_support/portable-atomic",
"bevy_utils/portable-atomic",
]

[dependencies]
# bevy
bevy_reflect_derive = { path = "derive", version = "0.16.0-dev" }
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_reflect/src/func/dynamic_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ impl<'env> DynamicFunction<'env> {
) -> Self {
let arc = Arc::new(func);

#[cfg(feature = "portable-atomic")]
#[cfg(not(target_has_atomic = "ptr"))]
#[expect(
unsafe_code,
reason = "unsized coercion is an unstable feature for non-std types"
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_reflect/src/generics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ impl ConstParamInfo {
pub fn with_default<T: Reflect + 'static>(mut self, default: T) -> Self {
let arc = Arc::new(default);

#[cfg(feature = "portable-atomic")]
#[cfg(not(target_has_atomic = "ptr"))]
#[expect(
unsafe_code,
reason = "unsized coercion is an unstable feature for non-std types"
Expand Down
5 changes: 0 additions & 5 deletions crates/bevy_reflect/src/impls/std.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,11 +217,6 @@ impl_reflect_opaque!(::core::num::Wrapping<T: Clone + Send + Sync>());
impl_reflect_opaque!(::core::num::Saturating<T: Clone + Send + Sync>());
impl_reflect_opaque!(::bevy_platform_support::sync::Arc<T: Send + Sync + ?Sized>);

// We check despite `portable-atomic` being enabled, if the standard library `Arc` is
// also available, and implement Reflect for it.
#[cfg(all(feature = "portable-atomic", target_has_atomic = "ptr"))]
impl_reflect_opaque!(::alloc::sync::Arc<T: Send + Sync + ?Sized>);

// `Serialize` and `Deserialize` only for platforms supported by serde:
// https://github.com/serde-rs/serde/blob/3ffb86fc70efd3d329519e2dddfa306cc04f167c/serde/src/de/impls.rs#L1732
#[cfg(all(any(unix, windows), feature = "std"))]
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_reflect/src/serde/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ mod tests {
fn create_arc_dyn_enemy<T: Enemy>(enemy: T) -> Arc<dyn Enemy> {
let arc = Arc::new(enemy);

#[cfg(feature = "portable-atomic")]
#[cfg(not(target_has_atomic = "ptr"))]
#[expect(
unsafe_code,
reason = "unsized coercion is an unstable feature for non-std types"
Expand Down
10 changes: 0 additions & 10 deletions crates/bevy_state/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,6 @@ critical-section = [
"bevy_platform_support/critical-section",
]

## `portable-atomic` provides additional platform support for atomic types and
## operations, even on targets without native support.
portable-atomic = [
"bevy_ecs/portable-atomic",
"bevy_utils/portable-atomic",
"bevy_app?/portable-atomic",
"bevy_reflect?/portable-atomic",
"bevy_platform_support/portable-atomic",
]

[dependencies]
# bevy
bevy_ecs = { path = "../bevy_ecs", version = "0.16.0-dev", default-features = false }
Expand Down
13 changes: 8 additions & 5 deletions crates/bevy_tasks/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,6 @@ critical-section = [
"bevy_platform_support/critical-section",
"edge-executor?/critical-section",
]
portable-atomic = [
"bevy_platform_support/portable-atomic",
"edge-executor?/portable-atomic",
"async-task/portable-atomic",
]

[dependencies]
bevy_platform_support = { path = "../bevy_platform_support", version = "0.16.0-dev", default-features = false, features = [
Expand All @@ -54,6 +49,14 @@ wasm-bindgen-futures = "0.4"
pin-project = "1"
futures-channel = "0.3"

[target.'cfg(not(all(target_has_atomic = "8", target_has_atomic = "16", target_has_atomic = "32", target_has_atomic = "64", target_has_atomic = "ptr")))'.dependencies]
async-task = { version = "4.4.0", default-features = false, features = [
"portable-atomic",
] }
edge-executor = { version = "0.4.1", default-features = false, optional = true, features = [
"portable-atomic",
] }

[dev-dependencies]
web-time = { version = "1.1" }

Expand Down
Loading

0 comments on commit 76e9bf9

Please sign in to comment.