Skip to content

Commit

Permalink
Simplify atomic availability detection.
Browse files Browse the repository at this point in the history
- `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.
  • Loading branch information
Dirbaio committed Oct 30, 2023
1 parent db324a3 commit 67e76eb
Show file tree
Hide file tree
Showing 8 changed files with 60 additions and 142 deletions.
16 changes: 6 additions & 10 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
39 changes: 18 additions & 21 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 larger MPMC sizes.
mpmc_large = []
# This flag has no version guarantee, the `defmt` dependency can be updated in a patch release
defmt-impl = ["defmt"]
# 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"]

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

[target.riscv32i-unknown-none-elf.dependencies]
portable-atomic = { version = "1.0" }
# 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"]

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

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

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

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

[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 }
Expand Down
87 changes: 13 additions & 74 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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") {
Expand Down
14 changes: 0 additions & 14 deletions no-atomics.sh

This file was deleted.

14 changes: 0 additions & 14 deletions no-cas.sh

This file was deleted.

24 changes: 19 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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/
//!
Expand Down Expand Up @@ -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;
4 changes: 2 additions & 2 deletions src/mpmc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions src/spsc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down

0 comments on commit 67e76eb

Please sign in to comment.