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.
This commit is contained in:
Dario Nieuwenhuis 2023-10-30 23:46:49 +01:00
parent db324a3ae0
commit f58e509664
9 changed files with 87 additions and 169 deletions

View File

@ -80,13 +80,6 @@ jobs:
- thumbv7m-none-eabi
- thumbv8m.base-none-eabi
- thumbv8m.main-none-eabi
toolchain:
- stable
- nightly
features:
- ""
- "cas,portable-atomic/critical-section"
- "serde"
steps:
- name: Checkout
uses: actions/checkout@v4
@ -113,14 +106,17 @@ jobs:
${{ runner.OS }}-build-${{ hashFiles('**/Cargo.lock') }}
${{ runner.OS }}-build-
- name: Install Rust ${{ matrix.toolchain }} with target (${{ matrix.target }})
- name: Install Rust with target (${{ matrix.target }})
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.toolchain }}
toolchain: stable
targets: ${{ matrix.target }}
- name: cargo check
run: cargo check --target=${{ matrix.target }} --no-default-features --features=${{ matrix.features }}
run: |
cargo check --target=${{ matrix.target }}
cargo check --target=${{ matrix.target }} --features="portable-atomic-critical-section"
cargo check --target=${{ matrix.target }} --features="ufmt serde defmt-03 mpmc_large"
doc:
name: doc
@ -130,10 +126,6 @@ jobs:
target:
- x86_64-unknown-linux-gnu
- thumbv7m-none-eabi
features:
- ""
- "cas"
- "serde"
steps:
- name: Checkout
uses: actions/checkout@v4
@ -166,7 +158,7 @@ jobs:
targets: ${{ matrix.target }}
- name: cargo doc
run: cargo doc --target=${{ matrix.target }} --no-default-features --features=${{ matrix.features }}
run: cargo doc --target=${{ matrix.target }} --features="ufmt serde defmt-03 mpmc_large portable-atomic-critical-section"
# Run cpass tests
testcpass:

View File

@ -34,6 +34,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- [breaking-change] this crate now uses `portable-atomic` v1.0 instead of `atomic-polyfill` for emulating
CAS instructions on targets where they're not natively available.
- [breaking-change] `From<&str>` for `String` was replaced with `TryFrom<&str>` because the `From` trait must not fail.
- [breaking-change] Renamed Cargo features
- `defmt-impl` is now `defmt-03`
- `ufmt-impl` is now `ufmt`
- `cas` is removed, atomic polyfilling is now opt-in via the `portable-atomic` feature.
### Fixed
@ -91,31 +95,31 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Added
* Added support for AVR architecture.
* Add `entry` API to `IndexMap`
* Implement `IntoIterator` trait for `Indexmap`
* Implement `FromIterator` for `String`
* Add `first` and `last` methods to `IndexMap` and `IndexSet`
* Add `pop_{front_back}_unchecked` methods to `Deque`
- Added support for AVR architecture.
- Add `entry` API to `IndexMap`
- Implement `IntoIterator` trait for `Indexmap`
- Implement `FromIterator` for `String`
- Add `first` and `last` methods to `IndexMap` and `IndexSet`
- Add `pop_{front_back}_unchecked` methods to `Deque`
### Changed
* Optimize the codegen of `Vec::clone`
* `riscv32i` and `riscv32imc` targets unconditionally (e.g. `build --no-default-features`) depends on `atomic-polyfill`
- Optimize the codegen of `Vec::clone`
- `riscv32i` and `riscv32imc` targets unconditionally (e.g. `build --no-default-features`) depends on `atomic-polyfill`
### Fixed
* Inserting an item that replaces an already present item will no longer
fail with an error
- Inserting an item that replaces an already present item will no longer
fail with an error
## [v0.7.11] - 2022-05-09
### Fixed
* Fixed `pool` example in docstring.
* Fixed undefined behavior in `Vec::truncate()`, `Vec::swap_remove_unchecked()`,
- Fixed `pool` example in docstring.
- Fixed undefined behavior in `Vec::truncate()`, `Vec::swap_remove_unchecked()`,
and `Hole::move_to()` (internal to the binary heap implementation).
* Fixed `BinaryHeap` elements are being dropped twice
- Fixed `BinaryHeap` elements are being dropped twice
## [v0.7.10] - 2022-01-21
@ -295,8 +299,8 @@ fail with an error
### Added
- opt-out `cas` feature to disable parts of the API that use CAS instructions.
Useful if using a custom (i.e. not built-in) rustc target that does not have CAS
instructions.
Useful if using a custom (i.e. not built-in) rustc target that does not have CAS
instructions.
- singleton `Pool` support on ARMv7-A devices
@ -315,7 +319,7 @@ instructions.
- `Pool` now implements the `Sync` trait when targeting ARMv7-R.
- Most data structures can now be constructed in "const context" (e.g. `static
[mut]` variables) using a newtype in `heapless::i`.
[mut]` variables) using a newtype in `heapless::i`.
- `Pool` has gained a `grow_exact` method to more efficiently use statically
allocated memory.
@ -360,7 +364,7 @@ instructions.
### Added
- Added a memory pool that's lock-free and interrupt-safe on the ARMv7-M
architecture.
architecture.
- `IndexMap` have gained `Eq` and `PartialEq` implementations.
@ -548,7 +552,7 @@ architecture.
- [breaking-change] The error type of all operations that may fail has changed from `()` to
`BufferFullError`.
- Both `RingBuffer` and `Vec` now support arrays of *any* size for their backup storage.
- Both `RingBuffer` and `Vec` now support arrays of _any_ size for their backup storage.
## [v0.1.0] - 2017-04-27

View File

@ -15,35 +15,32 @@ repository = "https://github.com/rust-embedded/heapless"
version = "0.8.0"
[features]
default = ["cas"]
cas = ["portable-atomic"]
ufmt-impl = ["ufmt-write"]
# only for tests
__trybuild = []
# Enable polyfilling of atomics via `portable-atomic`.
# `portable-atomic` polyfills some functionality by default, but to get full atomics you must
# enable one of its features to tell it how to do it. See `portable-atomic` documentation for details.
portable-atomic = ["dep:portable-atomic"]
# Enable polyfilling of atomics via portable-atomic, using critical section for locking
portable-atomic-critical-section = ["dep:portable-atomic", "portable-atomic?/critical-section"]
# Enable polyfilling of atomics via portable-atomic, using disabling interrupts for locking.
# WARNING: this is only sound for single-core bare-metal privileged-mode targets!
portable-atomic-unsafe-assume-single-core = ["dep:portable-atomic", "portable-atomic?/unsafe-assume-single-core"]
# implement serde traits.
serde = ["dep:serde"]
# implement ufmt traits.
ufmt = ["dep:ufmt-write"]
# Implement defmt::Format from defmt v0.3
defmt-03 = ["dep:defmt"]
# Enable larger MPMC sizes.
mpmc_large = []
# This flag has no version guarantee, the `defmt` dependency can be updated in a patch release
defmt-impl = ["defmt"]
[target.thumbv6m-none-eabi.dependencies]
portable-atomic = { version = "1.0", optional = true }
[target.riscv32i-unknown-none-elf.dependencies]
portable-atomic = { version = "1.0" }
[target.riscv32imc-unknown-none-elf.dependencies]
portable-atomic = { version = "1.0" }
[target.msp430-none-elf.dependencies]
portable-atomic = { version = "1.0" }
[target.xtensa-esp32s2-none-elf.dependencies]
portable-atomic = { version = "1.0", optional = true }
[target.'cfg(target_arch = "avr")'.dependencies]
portable-atomic = { version = "1.0", optional = true }
[dependencies]
portable-atomic = { version = "1.0", optional = true }
hash32 = "0.3.0"
serde = { version = "1", optional = true, default-features = false }
stable_deref_trait = { version = "1", default-features = false }

View File

@ -11,82 +11,21 @@ use std::{
fn main() -> Result<(), Box<dyn Error>> {
let target = env::var("TARGET")?;
if target.starts_with("thumbv6m-") {
println!("cargo:rustc-cfg=armv6m");
} else if target.starts_with("thumbv7m-") {
println!("cargo:rustc-cfg=armv7m");
} else if target.starts_with("thumbv7em-") {
println!("cargo:rustc-cfg=armv7m");
} else if target.starts_with("armv7r-") | target.starts_with("armebv7r-") {
println!("cargo:rustc-cfg=armv7r");
} else if target.starts_with("thumbv8m.base") {
println!("cargo:rustc-cfg=armv8m_base");
} else if target.starts_with("thumbv8m.main") {
println!("cargo:rustc-cfg=armv8m_main");
} else if target.starts_with("armv7-") | target.starts_with("armv7a-") {
println!("cargo:rustc-cfg=armv7a");
}
let is_avr = env::var("CARGO_CFG_TARGET_ARCH").as_deref() == Ok("avr");
// Set some cfg's depending on the target.
// - has_atomics: atomic load/store is available (either natively or through portable-atomic)
// - has_cas: atomic CAS is available (either natively or through portable-atomic)
// - use_portable_atomic: Use portable-atomic for all atomics (load/store and CAS).
// - use_portable_atomic_cas: Use portable-atomic for CAS atomic operations. Load/store can keep using core::sync:atomic.
// built-in targets with no atomic / CAS support as of nightly-2022-01-13
// AND not supported by the portable-atomic crate
// see the `no-atomics.sh` / `no-cas.sh` script sitting next to this file
if is_avr {
// lacks cas
} else {
match &target[..] {
"avr-unknown-gnu-atmega328"
| "bpfeb-unknown-none"
| "bpfel-unknown-none"
// | "msp430-none-elf" // supported by portable-atomic
// | "riscv32i-unknown-none-elf" // supported by portable-atomic
// | "riscv32imc-unknown-none-elf" // supported by portable-atomic
// | "thumbv4t-none-eabi" // supported by portable-atomic
// | "thumbv6m-none-eabi" // supported by portable-atomic
=> {}
_ => {
println!("cargo:rustc-cfg=has_cas");
}
}
// Manually list targets that have atomic load/store, but no CAS.
// Remove when `cfg(target_has_atomic_load_store)` is stable.
// last updated nightly-2023-10-28
match &target[..] {
"armv4t-none-eabi"
| "armv5te-none-eabi"
| "avr-unknown-gnu-atmega328"
| "bpfeb-unknown-none"
| "bpfel-unknown-none"
| "thumbv4t-none-eabi"
| "thumbv5te-none-eabi"
| "thumbv6m-none-eabi" => println!("cargo:rustc-cfg=has_atomic_load_store"),
_ => {}
};
if is_avr {
// lacks atomics
} else {
println!("cargo:rustc-cfg=has_atomics");
}
// Let the code know if it should use portable-atomic or not, for either
// only CAS, or for all atomics.
if is_avr {
println!("cargo:rustc-cfg=use_portable_atomic");
println!("cargo:rustc-cfg=use_portable_atomic_cas");
} else {
match &target[..] {
"riscv32i-unknown-none-elf"
| "riscv32imc-unknown-none-elf"
| "xtensa-esp32s2-none-elf"
| "thumbv4t-none-eabi"
| "msp430-none-elf" => {
println!("cargo:rustc-cfg=use_portable_atomic");
println!("cargo:rustc-cfg=use_portable_atomic_cas");
}
"thumbv6m-none-eabi" => {
println!("cargo:rustc-cfg=use_portable_atomic_cas");
}
_ => {}
}
}
// AArch64 instruction set contains `clrex` but not `ldrex` or `strex`; the
// probe will succeed when we already know to deny this target from LLSC.
if !target.starts_with("aarch64") {

View File

@ -1,14 +0,0 @@
#!/bin/bash
set -euo pipefail
main() {
IFS='
'
for t in $(rustc --print target-list); do
rustc +nightly --print cfg --target $t | grep 'target_has_atomic_load_store=' >/dev/null || echo $t
done
}
main

View File

@ -1,14 +0,0 @@
#!/bin/bash
set -euo pipefail
main() {
IFS='
'
for t in $(rustc --print target-list); do
rustc +nightly --print cfg --target $t | grep 'target_has_atomic=' >/dev/null || echo $t
done
}
main

View File

@ -61,7 +61,7 @@
//!
//! The `heapless` crate provides the following optional Cargo features:
//!
//! - `ufmt-impl`: Implement [`ufmt_write::uWrite`] for `String<N>` and `Vec<u8, N>`
//! - `ufmt`: Implement [`ufmt_write::uWrite`] for `String<N>` and `Vec<u8, N>`
//!
//! [`ufmt_write::uWrite`]: https://docs.rs/ufmt-write/
//!
@ -108,17 +108,31 @@ mod de;
mod ser;
pub mod binary_heap;
#[cfg(feature = "defmt-impl")]
#[cfg(feature = "defmt-03")]
mod defmt;
#[cfg(all(has_cas, feature = "cas"))]
#[cfg(any(
// assume we have all atomics available if we're using portable-atomic
feature = "portable-atomic",
// target has native atomic CAS (mpmc_large requires usize, otherwise just u8)
all(feature = "mpmc_large", target_has_atomic = "ptr"),
all(not(feature = "mpmc_large"), target_has_atomic = "8")
))]
pub mod mpmc;
#[cfg(any(arm_llsc, target_arch = "x86"))]
pub mod pool;
pub mod sorted_linked_list;
#[cfg(has_atomics)]
#[cfg(any(
// assume we have all atomics available if we're using portable-atomic
feature = "portable-atomic",
// target has native atomic CAS. Note this is too restrictive, spsc requires load/store only, not CAS.
// This should be `cfg(target_has_atomic_load_store)`, but that's not stable yet.
target_has_atomic = "ptr",
// or the current target is in a list in build.rs of targets known to have load/store but no CAS.
has_atomic_load_store
))]
pub mod spsc;
#[cfg(feature = "ufmt-impl")]
#[cfg(feature = "ufmt")]
mod ufmt;
mod sealed;

View File

@ -88,9 +88,9 @@
use core::{cell::UnsafeCell, mem::MaybeUninit};
#[cfg(not(use_portable_atomic_cas))]
#[cfg(not(feature = "portable-atomic"))]
use core::sync::atomic;
#[cfg(use_portable_atomic_cas)]
#[cfg(feature = "portable-atomic")]
use portable_atomic as atomic;
use atomic::Ordering;

View File

@ -96,9 +96,9 @@
use core::{cell::UnsafeCell, fmt, hash, mem::MaybeUninit, ptr};
#[cfg(not(use_portable_atomic))]
#[cfg(not(feature = "portable-atomic"))]
use core::sync::atomic;
#[cfg(use_portable_atomic)]
#[cfg(feature = "portable-atomic")]
use portable_atomic as atomic;
use atomic::{AtomicUsize, Ordering};