Skip to content

Commit f58e509

Browse files
committed
Simplify atomic availability detection.
- `cfg(target_has_atomic)` is stable now, use that. - Hardcode in `build.rs` the list of targets with load/store but no CAS, since `cfg(target_has_atomic_load_store)` is not stable yet. - Do not try to autodetect whether `portable-atomic` is needed or not, just let the user control it directly. If the user doesn't explicitly enable `portable-atomic` and native atomics are unavailable, the features requiring it will be missing.
1 parent db324a3 commit f58e509

File tree

9 files changed

+83
-165
lines changed

9 files changed

+83
-165
lines changed

.github/workflows/build.yml

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,6 @@ jobs:
8080
- thumbv7m-none-eabi
8181
- thumbv8m.base-none-eabi
8282
- thumbv8m.main-none-eabi
83-
toolchain:
84-
- stable
85-
- nightly
86-
features:
87-
- ""
88-
- "cas,portable-atomic/critical-section"
89-
- "serde"
9083
steps:
9184
- name: Checkout
9285
uses: actions/checkout@v4
@@ -113,14 +106,17 @@ jobs:
113106
${{ runner.OS }}-build-${{ hashFiles('**/Cargo.lock') }}
114107
${{ runner.OS }}-build-
115108
116-
- name: Install Rust ${{ matrix.toolchain }} with target (${{ matrix.target }})
109+
- name: Install Rust with target (${{ matrix.target }})
117110
uses: dtolnay/rust-toolchain@master
118111
with:
119-
toolchain: ${{ matrix.toolchain }}
112+
toolchain: stable
120113
targets: ${{ matrix.target }}
121114

122115
- name: cargo check
123-
run: cargo check --target=${{ matrix.target }} --no-default-features --features=${{ matrix.features }}
116+
run: |
117+
cargo check --target=${{ matrix.target }}
118+
cargo check --target=${{ matrix.target }} --features="portable-atomic-critical-section"
119+
cargo check --target=${{ matrix.target }} --features="ufmt serde defmt-03 mpmc_large"
124120
125121
doc:
126122
name: doc
@@ -130,10 +126,6 @@ jobs:
130126
target:
131127
- x86_64-unknown-linux-gnu
132128
- thumbv7m-none-eabi
133-
features:
134-
- ""
135-
- "cas"
136-
- "serde"
137129
steps:
138130
- name: Checkout
139131
uses: actions/checkout@v4
@@ -166,7 +158,7 @@ jobs:
166158
targets: ${{ matrix.target }}
167159

168160
- name: cargo doc
169-
run: cargo doc --target=${{ matrix.target }} --no-default-features --features=${{ matrix.features }}
161+
run: cargo doc --target=${{ matrix.target }} --features="ufmt serde defmt-03 mpmc_large portable-atomic-critical-section"
170162

171163
# Run cpass tests
172164
testcpass:

CHANGELOG.md

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
3434
- [breaking-change] this crate now uses `portable-atomic` v1.0 instead of `atomic-polyfill` for emulating
3535
CAS instructions on targets where they're not natively available.
3636
- [breaking-change] `From<&str>` for `String` was replaced with `TryFrom<&str>` because the `From` trait must not fail.
37+
- [breaking-change] Renamed Cargo features
38+
- `defmt-impl` is now `defmt-03`
39+
- `ufmt-impl` is now `ufmt`
40+
- `cas` is removed, atomic polyfilling is now opt-in via the `portable-atomic` feature.
3741

3842
### Fixed
3943

@@ -91,31 +95,31 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
9195

9296
### Added
9397

94-
* Added support for AVR architecture.
95-
* Add `entry` API to `IndexMap`
96-
* Implement `IntoIterator` trait for `Indexmap`
97-
* Implement `FromIterator` for `String`
98-
* Add `first` and `last` methods to `IndexMap` and `IndexSet`
99-
* Add `pop_{front_back}_unchecked` methods to `Deque`
98+
- Added support for AVR architecture.
99+
- Add `entry` API to `IndexMap`
100+
- Implement `IntoIterator` trait for `Indexmap`
101+
- Implement `FromIterator` for `String`
102+
- Add `first` and `last` methods to `IndexMap` and `IndexSet`
103+
- Add `pop_{front_back}_unchecked` methods to `Deque`
100104

101105
### Changed
102106

103-
* Optimize the codegen of `Vec::clone`
104-
* `riscv32i` and `riscv32imc` targets unconditionally (e.g. `build --no-default-features`) depends on `atomic-polyfill`
107+
- Optimize the codegen of `Vec::clone`
108+
- `riscv32i` and `riscv32imc` targets unconditionally (e.g. `build --no-default-features`) depends on `atomic-polyfill`
105109

106110
### Fixed
107111

108-
* Inserting an item that replaces an already present item will no longer
109-
fail with an error
112+
- Inserting an item that replaces an already present item will no longer
113+
fail with an error
110114

111115
## [v0.7.11] - 2022-05-09
112116

113117
### Fixed
114118

115-
* Fixed `pool` example in docstring.
116-
* Fixed undefined behavior in `Vec::truncate()`, `Vec::swap_remove_unchecked()`,
119+
- Fixed `pool` example in docstring.
120+
- Fixed undefined behavior in `Vec::truncate()`, `Vec::swap_remove_unchecked()`,
117121
and `Hole::move_to()` (internal to the binary heap implementation).
118-
* Fixed `BinaryHeap` elements are being dropped twice
122+
- Fixed `BinaryHeap` elements are being dropped twice
119123

120124
## [v0.7.10] - 2022-01-21
121125

@@ -295,8 +299,8 @@ fail with an error
295299
### Added
296300

297301
- opt-out `cas` feature to disable parts of the API that use CAS instructions.
298-
Useful if using a custom (i.e. not built-in) rustc target that does not have CAS
299-
instructions.
302+
Useful if using a custom (i.e. not built-in) rustc target that does not have CAS
303+
instructions.
300304

301305
- singleton `Pool` support on ARMv7-A devices
302306

@@ -315,7 +319,7 @@ instructions.
315319
- `Pool` now implements the `Sync` trait when targeting ARMv7-R.
316320

317321
- Most data structures can now be constructed in "const context" (e.g. `static
318-
[mut]` variables) using a newtype in `heapless::i`.
322+
[mut]` variables) using a newtype in `heapless::i`.
319323

320324
- `Pool` has gained a `grow_exact` method to more efficiently use statically
321325
allocated memory.
@@ -360,7 +364,7 @@ instructions.
360364
### Added
361365

362366
- Added a memory pool that's lock-free and interrupt-safe on the ARMv7-M
363-
architecture.
367+
architecture.
364368

365369
- `IndexMap` have gained `Eq` and `PartialEq` implementations.
366370

@@ -548,7 +552,7 @@ architecture.
548552
- [breaking-change] The error type of all operations that may fail has changed from `()` to
549553
`BufferFullError`.
550554

551-
- Both `RingBuffer` and `Vec` now support arrays of *any* size for their backup storage.
555+
- Both `RingBuffer` and `Vec` now support arrays of _any_ size for their backup storage.
552556

553557
## [v0.1.0] - 2017-04-27
554558

Cargo.toml

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,35 +15,32 @@ repository = "https://github.com/rust-embedded/heapless"
1515
version = "0.8.0"
1616

1717
[features]
18-
default = ["cas"]
19-
cas = ["portable-atomic"]
20-
ufmt-impl = ["ufmt-write"]
21-
# only for tests
22-
__trybuild = []
23-
# Enable larger MPMC sizes.
24-
mpmc_large = []
25-
# This flag has no version guarantee, the `defmt` dependency can be updated in a patch release
26-
defmt-impl = ["defmt"]
18+
# Enable polyfilling of atomics via `portable-atomic`.
19+
# `portable-atomic` polyfills some functionality by default, but to get full atomics you must
20+
# enable one of its features to tell it how to do it. See `portable-atomic` documentation for details.
21+
portable-atomic = ["dep:portable-atomic"]
2722

28-
[target.thumbv6m-none-eabi.dependencies]
29-
portable-atomic = { version = "1.0", optional = true }
23+
# Enable polyfilling of atomics via portable-atomic, using critical section for locking
24+
portable-atomic-critical-section = ["dep:portable-atomic", "portable-atomic?/critical-section"]
3025

31-
[target.riscv32i-unknown-none-elf.dependencies]
32-
portable-atomic = { version = "1.0" }
26+
# Enable polyfilling of atomics via portable-atomic, using disabling interrupts for locking.
27+
# WARNING: this is only sound for single-core bare-metal privileged-mode targets!
28+
portable-atomic-unsafe-assume-single-core = ["dep:portable-atomic", "portable-atomic?/unsafe-assume-single-core"]
3329

34-
[target.riscv32imc-unknown-none-elf.dependencies]
35-
portable-atomic = { version = "1.0" }
30+
# implement serde traits.
31+
serde = ["dep:serde"]
3632

37-
[target.msp430-none-elf.dependencies]
38-
portable-atomic = { version = "1.0" }
33+
# implement ufmt traits.
34+
ufmt = ["dep:ufmt-write"]
3935

40-
[target.xtensa-esp32s2-none-elf.dependencies]
41-
portable-atomic = { version = "1.0", optional = true }
36+
# Implement defmt::Format from defmt v0.3
37+
defmt-03 = ["dep:defmt"]
4238

43-
[target.'cfg(target_arch = "avr")'.dependencies]
44-
portable-atomic = { version = "1.0", optional = true }
39+
# Enable larger MPMC sizes.
40+
mpmc_large = []
4541

4642
[dependencies]
43+
portable-atomic = { version = "1.0", optional = true }
4744
hash32 = "0.3.0"
4845
serde = { version = "1", optional = true, default-features = false }
4946
stable_deref_trait = { version = "1", default-features = false }

build.rs

Lines changed: 13 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -11,82 +11,21 @@ use std::{
1111
fn main() -> Result<(), Box<dyn Error>> {
1212
let target = env::var("TARGET")?;
1313

14-
if target.starts_with("thumbv6m-") {
15-
println!("cargo:rustc-cfg=armv6m");
16-
} else if target.starts_with("thumbv7m-") {
17-
println!("cargo:rustc-cfg=armv7m");
18-
} else if target.starts_with("thumbv7em-") {
19-
println!("cargo:rustc-cfg=armv7m");
20-
} else if target.starts_with("armv7r-") | target.starts_with("armebv7r-") {
21-
println!("cargo:rustc-cfg=armv7r");
22-
} else if target.starts_with("thumbv8m.base") {
23-
println!("cargo:rustc-cfg=armv8m_base");
24-
} else if target.starts_with("thumbv8m.main") {
25-
println!("cargo:rustc-cfg=armv8m_main");
26-
} else if target.starts_with("armv7-") | target.starts_with("armv7a-") {
27-
println!("cargo:rustc-cfg=armv7a");
28-
}
29-
30-
let is_avr = env::var("CARGO_CFG_TARGET_ARCH").as_deref() == Ok("avr");
31-
32-
// Set some cfg's depending on the target.
33-
// - has_atomics: atomic load/store is available (either natively or through portable-atomic)
34-
// - has_cas: atomic CAS is available (either natively or through portable-atomic)
35-
// - use_portable_atomic: Use portable-atomic for all atomics (load/store and CAS).
36-
// - use_portable_atomic_cas: Use portable-atomic for CAS atomic operations. Load/store can keep using core::sync:atomic.
37-
38-
// built-in targets with no atomic / CAS support as of nightly-2022-01-13
39-
// AND not supported by the portable-atomic crate
40-
// see the `no-atomics.sh` / `no-cas.sh` script sitting next to this file
41-
if is_avr {
42-
// lacks cas
43-
} else {
44-
match &target[..] {
45-
"avr-unknown-gnu-atmega328"
46-
| "bpfeb-unknown-none"
47-
| "bpfel-unknown-none"
48-
// | "msp430-none-elf" // supported by portable-atomic
49-
// | "riscv32i-unknown-none-elf" // supported by portable-atomic
50-
// | "riscv32imc-unknown-none-elf" // supported by portable-atomic
51-
// | "thumbv4t-none-eabi" // supported by portable-atomic
52-
// | "thumbv6m-none-eabi" // supported by portable-atomic
53-
=> {}
54-
55-
_ => {
56-
println!("cargo:rustc-cfg=has_cas");
57-
}
58-
}
14+
// Manually list targets that have atomic load/store, but no CAS.
15+
// Remove when `cfg(target_has_atomic_load_store)` is stable.
16+
// last updated nightly-2023-10-28
17+
match &target[..] {
18+
"armv4t-none-eabi"
19+
| "armv5te-none-eabi"
20+
| "avr-unknown-gnu-atmega328"
21+
| "bpfeb-unknown-none"
22+
| "bpfel-unknown-none"
23+
| "thumbv4t-none-eabi"
24+
| "thumbv5te-none-eabi"
25+
| "thumbv6m-none-eabi" => println!("cargo:rustc-cfg=has_atomic_load_store"),
26+
_ => {}
5927
};
6028

61-
if is_avr {
62-
// lacks atomics
63-
} else {
64-
println!("cargo:rustc-cfg=has_atomics");
65-
}
66-
67-
// Let the code know if it should use portable-atomic or not, for either
68-
// only CAS, or for all atomics.
69-
if is_avr {
70-
println!("cargo:rustc-cfg=use_portable_atomic");
71-
println!("cargo:rustc-cfg=use_portable_atomic_cas");
72-
} else {
73-
match &target[..] {
74-
"riscv32i-unknown-none-elf"
75-
| "riscv32imc-unknown-none-elf"
76-
| "xtensa-esp32s2-none-elf"
77-
| "thumbv4t-none-eabi"
78-
| "msp430-none-elf" => {
79-
println!("cargo:rustc-cfg=use_portable_atomic");
80-
println!("cargo:rustc-cfg=use_portable_atomic_cas");
81-
}
82-
83-
"thumbv6m-none-eabi" => {
84-
println!("cargo:rustc-cfg=use_portable_atomic_cas");
85-
}
86-
_ => {}
87-
}
88-
}
89-
9029
// AArch64 instruction set contains `clrex` but not `ldrex` or `strex`; the
9130
// probe will succeed when we already know to deny this target from LLSC.
9231
if !target.starts_with("aarch64") {

no-atomics.sh

Lines changed: 0 additions & 14 deletions
This file was deleted.

no-cas.sh

Lines changed: 0 additions & 14 deletions
This file was deleted.

src/lib.rs

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161
//!
6262
//! The `heapless` crate provides the following optional Cargo features:
6363
//!
64-
//! - `ufmt-impl`: Implement [`ufmt_write::uWrite`] for `String<N>` and `Vec<u8, N>`
64+
//! - `ufmt`: Implement [`ufmt_write::uWrite`] for `String<N>` and `Vec<u8, N>`
6565
//!
6666
//! [`ufmt_write::uWrite`]: https://docs.rs/ufmt-write/
6767
//!
@@ -108,17 +108,31 @@ mod de;
108108
mod ser;
109109

110110
pub mod binary_heap;
111-
#[cfg(feature = "defmt-impl")]
111+
#[cfg(feature = "defmt-03")]
112112
mod defmt;
113-
#[cfg(all(has_cas, feature = "cas"))]
113+
#[cfg(any(
114+
// assume we have all atomics available if we're using portable-atomic
115+
feature = "portable-atomic",
116+
// target has native atomic CAS (mpmc_large requires usize, otherwise just u8)
117+
all(feature = "mpmc_large", target_has_atomic = "ptr"),
118+
all(not(feature = "mpmc_large"), target_has_atomic = "8")
119+
))]
114120
pub mod mpmc;
115121
#[cfg(any(arm_llsc, target_arch = "x86"))]
116122
pub mod pool;
117123
pub mod sorted_linked_list;
118-
#[cfg(has_atomics)]
124+
#[cfg(any(
125+
// assume we have all atomics available if we're using portable-atomic
126+
feature = "portable-atomic",
127+
// target has native atomic CAS. Note this is too restrictive, spsc requires load/store only, not CAS.
128+
// This should be `cfg(target_has_atomic_load_store)`, but that's not stable yet.
129+
target_has_atomic = "ptr",
130+
// or the current target is in a list in build.rs of targets known to have load/store but no CAS.
131+
has_atomic_load_store
132+
))]
119133
pub mod spsc;
120134

121-
#[cfg(feature = "ufmt-impl")]
135+
#[cfg(feature = "ufmt")]
122136
mod ufmt;
123137

124138
mod sealed;

src/mpmc.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,9 @@
8888
8989
use core::{cell::UnsafeCell, mem::MaybeUninit};
9090

91-
#[cfg(not(use_portable_atomic_cas))]
91+
#[cfg(not(feature = "portable-atomic"))]
9292
use core::sync::atomic;
93-
#[cfg(use_portable_atomic_cas)]
93+
#[cfg(feature = "portable-atomic")]
9494
use portable_atomic as atomic;
9595

9696
use atomic::Ordering;

src/spsc.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,9 @@
9696
9797
use core::{cell::UnsafeCell, fmt, hash, mem::MaybeUninit, ptr};
9898

99-
#[cfg(not(use_portable_atomic))]
99+
#[cfg(not(feature = "portable-atomic"))]
100100
use core::sync::atomic;
101-
#[cfg(use_portable_atomic)]
101+
#[cfg(feature = "portable-atomic")]
102102
use portable_atomic as atomic;
103103

104104
use atomic::{AtomicUsize, Ordering};

0 commit comments

Comments
 (0)