Skip to content

Commit 76e9bf9

Browse files
Automatically enable portable-atomic when required (#17570)
# 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).
1 parent 7f14581 commit 76e9bf9

File tree

21 files changed

+94
-152
lines changed

21 files changed

+94
-152
lines changed

crates/bevy_a11y/Cargo.toml

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,15 +50,6 @@ critical-section = [
5050
"bevy_input_focus/critical-section",
5151
]
5252

53-
## `portable-atomic` provides additional platform support for atomic types and
54-
## operations, even on targets without native support.
55-
portable-atomic = [
56-
"bevy_app/portable-atomic",
57-
"bevy_ecs/portable-atomic",
58-
"bevy_reflect?/portable-atomic",
59-
"bevy_input_focus/portable-atomic",
60-
]
61-
6253
## Uses the `libm` maths library instead of the one provided in `std` and `core`.
6354
libm = ["bevy_input_focus/libm"]
6455

crates/bevy_app/Cargo.toml

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -60,15 +60,6 @@ critical-section = [
6060
"bevy_reflect?/critical-section",
6161
]
6262

63-
## `portable-atomic` provides additional platform support for atomic types and
64-
## operations, even on targets without native support.
65-
portable-atomic = [
66-
"bevy_tasks?/portable-atomic",
67-
"bevy_ecs/portable-atomic",
68-
"bevy_platform_support/portable-atomic",
69-
"bevy_reflect?/portable-atomic",
70-
]
71-
7263
[dependencies]
7364
# bevy
7465
bevy_derive = { path = "../bevy_derive", version = "0.16.0-dev" }

crates/bevy_app/src/task_pool_plugin.rs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,3 @@
1-
#![cfg_attr(
2-
feature = "portable-atomic",
3-
expect(
4-
clippy::redundant_closure,
5-
reason = "bevy_platform_support::sync::Arc has subtly different implicit behavior"
6-
)
7-
)]
8-
91
use crate::{App, Plugin};
102

113
use alloc::string::ToString;

crates/bevy_diagnostic/Cargo.toml

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -54,17 +54,6 @@ critical-section = [
5454
"bevy_tasks?/critical-section",
5555
]
5656

57-
## `portable-atomic` provides additional platform support for atomic types and
58-
## operations, even on targets without native support.
59-
portable-atomic = [
60-
"bevy_ecs/portable-atomic",
61-
"bevy_app/portable-atomic",
62-
"bevy_platform_support/portable-atomic",
63-
"bevy_time/portable-atomic",
64-
"bevy_utils/portable-atomic",
65-
"bevy_tasks?/portable-atomic",
66-
]
67-
6857
[dependencies]
6958
# bevy
7059
bevy_app = { path = "../bevy_app", version = "0.16.0-dev", default-features = false }

crates/bevy_ecs/Cargo.toml

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -92,15 +92,6 @@ critical-section = [
9292
"bevy_reflect?/critical-section",
9393
]
9494

95-
## `portable-atomic` provides additional platform support for atomic types and
96-
## operations, even on targets without native support.
97-
portable-atomic = [
98-
"bevy_tasks?/portable-atomic",
99-
"bevy_platform_support/portable-atomic",
100-
"concurrent-queue/portable-atomic",
101-
"bevy_reflect?/portable-atomic",
102-
]
103-
10495
[dependencies]
10596
bevy_ptr = { path = "../bevy_ptr", version = "0.16.0-dev" }
10697
bevy_reflect = { path = "../bevy_reflect", version = "0.16.0-dev", features = [
@@ -139,6 +130,11 @@ tracing = { version = "0.1", default-features = false, optional = true }
139130
log = { version = "0.4", default-features = false }
140131
bumpalo = "3"
141132

133+
[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]
134+
concurrent-queue = { version = "2.5.0", default-features = false, features = [
135+
"portable-atomic",
136+
] }
137+
142138
[dev-dependencies]
143139
rand = "0.8"
144140
static_assertions = "1.1.0"

crates/bevy_ecs/src/component.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2060,7 +2060,7 @@ impl RequiredComponents {
20602060
//
20612061
// This would be resolved by https://github.com/rust-lang/rust/issues/123430
20622062

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

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

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

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

20812081
let boxed: Intermediate<Constructor> = Intermediate::new(

crates/bevy_input/Cargo.toml

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -56,15 +56,6 @@ critical-section = [
5656
"bevy_platform_support/critical-section",
5757
]
5858

59-
## `portable-atomic` provides additional platform support for atomic types and
60-
## operations, even on targets without native support.
61-
portable-atomic = [
62-
"bevy_app/portable-atomic",
63-
"bevy_ecs/portable-atomic",
64-
"bevy_reflect?/portable-atomic",
65-
"bevy_platform_support/portable-atomic",
66-
]
67-
6859
## Uses the `libm` maths library instead of the one provided in `std` and `core`.
6960
libm = ["bevy_math/libm"]
7061

crates/bevy_input_focus/Cargo.toml

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,15 +55,6 @@ critical-section = [
5555
"bevy_input/critical-section",
5656
]
5757

58-
## `portable-atomic` provides additional platform support for atomic types and
59-
## operations, even on targets without native support.
60-
portable-atomic = [
61-
"bevy_app/portable-atomic",
62-
"bevy_ecs/portable-atomic",
63-
"bevy_reflect?/portable-atomic",
64-
"bevy_input/portable-atomic",
65-
]
66-
6758
## Uses the `libm` maths library instead of the one provided in `std` and `core`.
6859
libm = ["bevy_math/libm", "bevy_window/libm"]
6960

crates/bevy_platform_support/Cargo.toml

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -24,32 +24,20 @@ serialize = ["hashbrown/serde"]
2424
std = [
2525
"alloc",
2626
"critical-section?/std",
27-
"portable-atomic?/std",
28-
"portable-atomic-util?/std",
27+
"portable-atomic/std",
28+
"portable-atomic-util/std",
2929
"spin/std",
3030
"foldhash/std",
3131
]
3232

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

3535
## `critical-section` provides the building blocks for synchronization primitives
3636
## on all platforms, including `no_std`.
37-
critical-section = ["dep:critical-section", "portable-atomic?/critical-section"]
38-
39-
## `portable-atomic` provides additional platform support for atomic types and
40-
## operations, even on targets without native support.
41-
portable-atomic = [
42-
"dep:portable-atomic",
43-
"dep:portable-atomic-util",
44-
"spin/portable_atomic",
45-
]
37+
critical-section = ["dep:critical-section", "portable-atomic/critical-section"]
4638

4739
[dependencies]
4840
critical-section = { version = "1.2.0", default-features = false, optional = true }
49-
portable-atomic = { version = "1", default-features = false, features = [
50-
"fallback",
51-
], optional = true }
52-
portable-atomic-util = { version = "0.2.4", default-features = false, optional = true }
5341
spin = { version = "0.9.8", default-features = false, features = [
5442
"mutex",
5543
"spin_mutex",
@@ -68,6 +56,17 @@ hashbrown = { version = "0.15.1", features = [
6856
web-time = { version = "1.1", default-features = false }
6957
getrandom = { version = "0.2.0", default-features = false, features = ["js"] }
7058

59+
[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]
60+
portable-atomic = { version = "1", default-features = false, features = [
61+
"fallback",
62+
] }
63+
spin = { version = "0.9.8", default-features = false, features = [
64+
"portable_atomic",
65+
] }
66+
67+
[target.'cfg(not(target_has_atomic = "ptr"))'.dependencies]
68+
portable-atomic-util = { version = "0.2.4", default-features = false }
69+
7170
[lints]
7271
workspace = true
7372

crates/bevy_platform_support/src/sync/atomic.rs

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,39 @@
55
//! Using these types will ensure the correct atomic provider is used without the need for
66
//! feature gates in your own code.
77
8-
pub use atomic::{
9-
AtomicBool, AtomicI16, AtomicI32, AtomicI64, AtomicI8, AtomicIsize, AtomicPtr, AtomicU16,
10-
AtomicU32, AtomicU64, AtomicU8, AtomicUsize, Ordering,
11-
};
8+
pub use atomic_16::{AtomicI16, AtomicU16};
9+
pub use atomic_32::{AtomicI32, AtomicU32};
10+
pub use atomic_64::{AtomicI64, AtomicU64};
11+
pub use atomic_8::{AtomicBool, AtomicI8, AtomicU8};
12+
pub use atomic_ptr::{AtomicIsize, AtomicPtr, AtomicUsize};
13+
pub use core::sync::atomic::Ordering;
1214

13-
#[cfg(not(feature = "portable-atomic"))]
14-
use core::sync::atomic;
15+
#[cfg(target_has_atomic = "8")]
16+
use core::sync::atomic as atomic_8;
1517

16-
#[cfg(feature = "portable-atomic")]
17-
use portable_atomic as atomic;
18+
#[cfg(not(target_has_atomic = "8"))]
19+
use portable_atomic as atomic_8;
20+
21+
#[cfg(target_has_atomic = "16")]
22+
use core::sync::atomic as atomic_16;
23+
24+
#[cfg(not(target_has_atomic = "16"))]
25+
use portable_atomic as atomic_16;
26+
27+
#[cfg(target_has_atomic = "32")]
28+
use core::sync::atomic as atomic_32;
29+
30+
#[cfg(not(target_has_atomic = "32"))]
31+
use portable_atomic as atomic_32;
32+
33+
#[cfg(target_has_atomic = "64")]
34+
use core::sync::atomic as atomic_64;
35+
36+
#[cfg(not(target_has_atomic = "64"))]
37+
use portable_atomic as atomic_64;
38+
39+
#[cfg(target_has_atomic = "ptr")]
40+
use core::sync::atomic as atomic_ptr;
41+
42+
#[cfg(not(target_has_atomic = "ptr"))]
43+
use portable_atomic as atomic_ptr;

0 commit comments

Comments
 (0)