diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e1947954be..d0431b64f0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -51,7 +51,6 @@ jobs: run: > MIRIFLAGS=-Zmiri-ignore-leaks cargo miri test --features="alloc, defmt, - mpmc_large, portable-atomic-critical-section, serde, ufmt, @@ -97,7 +96,6 @@ jobs: cargo test --features=" alloc, defmt, - mpmc_large, portable-atomic-critical-section, serde, ufmt, @@ -200,7 +198,6 @@ jobs: fi cargo check --target="${target}" --features="defmt" - cargo check --target="${target}" --features="mpmc_large" cargo check --target="${target}" --features="portable-atomic-critical-section" cargo check --target="${target}" --features="serde" cargo check --target="${target}" --features="ufmt" diff --git a/CHANGELOG.md b/CHANGELOG.md index e380d4c478..a1a94ff844 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Fixed unsoundness in `IndexMap:insert`. - Limited max size of `IndexMap` to u16::MAX + 1. The implementation for sizes higher than u16::MAX were unsound anyway. +- Removed `mpmc_large` feature. Targets that need `mpmc` must either support `target_has_atomic = + "ptr"` or use `portable-atomic`. +- Added `LenT` type parameter to `Queue` and `QueueView` with default value `u8` +- Added `wrapping_add` and `signed_wrapping_cmp` to `LenType` as provided methods +- Added `LenType::Atomic` and `LenType::Signed` associated types ## [v0.9.3] 2025-04-15 diff --git a/Cargo.toml b/Cargo.toml index a9e0cb857e..2b460950d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,9 +53,6 @@ zeroize = ["dep:zeroize"] # Implement `embedded_io::Write` "embedded-io-v0.7" = ["dep:embedded-io"] -# Enable larger MPMC sizes. -mpmc_large = [] - # Implement some alloc Vec interoperability alloc = [] @@ -86,7 +83,6 @@ features = [ "ufmt", "serde", "defmt", - "mpmc_large", "portable-atomic-critical-section", "alloc", ] diff --git a/cfail/ui/mpmc_capacity.rs b/cfail/ui/mpmc_capacity.rs new file mode 100644 index 0000000000..8b09565821 --- /dev/null +++ b/cfail/ui/mpmc_capacity.rs @@ -0,0 +1,10 @@ +use core::mem::ManuallyDrop; +use heapless::mpmc::Queue; + +const _: () = { + #[allow(deprecated)] + // 256 > u8::MAX + let _ = ManuallyDrop::new(Queue::::new()); +}; + +fn main() {} diff --git a/cfail/ui/mpmc_capacity.stderr b/cfail/ui/mpmc_capacity.stderr new file mode 100644 index 0000000000..40da099b00 --- /dev/null +++ b/cfail/ui/mpmc_capacity.stderr @@ -0,0 +1,15 @@ +error[E0080]: evaluation panicked: assertion failed: N < LenT::MAX_USIZE + --> $HEAPLESS/src/mpmc.rs + | + | assert!(N < LenT::MAX_USIZE); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `heapless::mpmc::QueueInner::>::new::{constant#0}` failed here + +note: erroneous constant encountered + --> $HEAPLESS/src/mpmc.rs + | + | / const { + | | assert!(N > 1); + | | assert!(N.is_power_of_two()); + | | assert!(N < LenT::MAX_USIZE); + | | } + | |_________^ diff --git a/src/len_type.rs b/src/len_type.rs index 7dd45c9102..216a4d31f9 100644 --- a/src/len_type.rs +++ b/src/len_type.rs @@ -1,9 +1,14 @@ use core::{ fmt::{Debug, Display}, mem, - ops::{Add, AddAssign, Sub, SubAssign}, + ops::{Add, AddAssign, BitAnd, Sub, SubAssign}, }; +#[cfg(not(feature = "portable-atomic"))] +use core::sync::atomic::{AtomicU16, AtomicU32, AtomicU8, AtomicUsize, Ordering}; +#[cfg(feature = "portable-atomic")] +use portable_atomic::{AtomicU16, AtomicU32, AtomicU8, AtomicUsize, Ordering}; + #[allow(non_camel_case_types)] pub enum TypeEnum { u8, @@ -24,9 +29,11 @@ pub trait Sealed: + PartialEq + Add + AddAssign + + BitAnd + Sub + SubAssign + PartialOrd + + Ord + TryFrom + TryInto { @@ -39,6 +46,11 @@ pub trait Sealed: /// This type as an enum. const TYPE: TypeEnum; + /// The corresponding atomic integer type. + type Atomic: Atomic; + /// The corresponding signed integer type. + type Signed; + /// The one value of the integer type. /// /// It's a function instead of constant because we want to have implementation which panics for @@ -66,16 +78,100 @@ pub trait Sealed: Some(self.into_usize()) } } + + /// Wrapping (modular) addition. Computes `self + rhs`, wrapping around at `Self::MAX_USIZE`. + #[inline] + fn wrapping_add(self, rhs: Self) -> Self { + Self::from_usize(self.into_usize().wrapping_add(rhs.into_usize()) & Self::MAX_USIZE) + } + + /// Compare `seq` and `expected_pos` as if they were signed integers, returning the result of + /// `(seq as Signed).wrapping_sub(expected_pos as Signed).cmp(&0)`. + #[inline] + fn signed_wrapping_cmp(seq: Self, expected_pos: Self) -> core::cmp::Ordering { + match seq.into_usize().wrapping_sub(expected_pos.into_usize()) { + 0 => core::cmp::Ordering::Equal, + d if d > (Self::MAX_USIZE / 2) => core::cmp::Ordering::Less, + _ => core::cmp::Ordering::Greater, + } + } +} + +// TODO consider replacing with stdlib version once generic_atomic lands in stable +// (https://github.com/rust-lang/rust/issues/130539) +pub trait Atomic { + /// Loads a value from the atomic integer. + /// + /// Behavior must be identical to the corresponding `load` implementation for the underlying + /// atomic type. + fn load(&self, order: Ordering) -> T; + /// Stores a value into the atomic integer. + /// + /// Behavior must be identical to the corresponding `store` implementation for the underlying + /// atomic type. + fn store(&self, val: T, order: Ordering); + /// Stores a value into the atomic integer if the current value is the same as the current + /// value. + /// + /// Behavior must be identical to the corresponding `compare_exchange_weak` implementation for + /// the underlying atomic type. + #[cfg(any(target_has_atomic = "ptr", feature = "portable-atomic"))] + fn compare_exchange_weak( + &self, + current: T, + new: T, + success: Ordering, + failure: Ordering, + ) -> Result; +} + +macro_rules! impl_atomic { + ($($(#[$meta:meta])* ($T:ty, $A:tt)),*) => {$( + impl Atomic<$T, $A> for $A { + fn load(&self, order: Ordering) -> $T { + self.load(order) + } + fn store(&self, val: $T, order: Ordering) { + self.store(val, order) + } + fn compare_exchange_weak(&self, current: $T, new: $T, success: Ordering, failure: Ordering) -> Result<$T, $T> { + self.compare_exchange_weak(current, new, success, failure) + } + } + )*} +} + +/// Converts a `usize` into the atomic type associated with the given `LenType`. +pub const fn new_atomic_lentype(val: usize) -> L::Atomic { + unsafe { + match L::TYPE { + TypeEnum::u8 => mem::transmute_copy(&AtomicU8::new(val as u8)), + TypeEnum::u16 => mem::transmute_copy(&AtomicU16::new(val as u16)), + TypeEnum::u32 => mem::transmute_copy(&AtomicU32::new(val as u32)), + TypeEnum::usize => mem::transmute_copy(&AtomicUsize::new(val)), + } + } } +impl_atomic!( + (u8, AtomicU8), + (u16, AtomicU16), + #[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))] + (u32, AtomicU32), + (usize, AtomicUsize) +); + macro_rules! impl_lentype { - ($($(#[$meta:meta])* $LenT:ident),*) => {$( + ($($(#[$meta:meta])* ($ULenT:tt, $SLenT:ty, $AULenT:ty)),*) => {$( $(#[$meta])* - impl Sealed for $LenT { + impl Sealed for $ULenT { const ZERO: Self = 0; const MAX: Self = Self::MAX; const MAX_USIZE: usize = Self::MAX as _; - const TYPE: TypeEnum = TypeEnum::$LenT; + const TYPE: TypeEnum = TypeEnum::$ULenT; + + type Atomic = $AULenT; + type Signed = $SLenT; fn one() -> Self { 1 @@ -83,7 +179,7 @@ macro_rules! impl_lentype { } $(#[$meta])* - impl LenType for $LenT {} + impl LenType for $ULenT {} )*} } @@ -102,11 +198,11 @@ pub trait LenType: Sealed + Zeroize {} pub trait LenType: Sealed {} impl_lentype!( - u8, - u16, + (u8, i8, AtomicU8), + (u16, i16, AtomicU16), #[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))] - u32, - usize + (u32, i32, AtomicU32), + (usize, isize, AtomicUsize) ); pub const fn check_capacity_fits() { diff --git a/src/lib.rs b/src/lib.rs index 2e561b865f..cefb0ef7b0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -192,13 +192,7 @@ pub mod binary_heap; mod bytes; #[cfg(feature = "defmt")] mod defmt; -#[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") -))] +#[cfg(any(feature = "portable-atomic", target_has_atomic = "ptr",))] pub mod mpmc; #[cfg(any( arm_llsc, diff --git a/src/mpmc.rs b/src/mpmc.rs index e64c0a94a6..c46a46b445 100644 --- a/src/mpmc.rs +++ b/src/mpmc.rs @@ -106,31 +106,20 @@ use portable_atomic as atomic; use atomic::Ordering; -use crate::storage::{OwnedStorage, Storage, ViewStorage}; - -#[cfg(feature = "mpmc_large")] -type AtomicTargetSize = atomic::AtomicUsize; -#[cfg(not(feature = "mpmc_large"))] -type AtomicTargetSize = atomic::AtomicU8; - -#[cfg(feature = "mpmc_large")] -type UintSize = usize; -#[cfg(not(feature = "mpmc_large"))] -type UintSize = u8; - -#[cfg(feature = "mpmc_large")] -type IntSize = isize; -#[cfg(not(feature = "mpmc_large"))] -type IntSize = i8; +use crate::{ + len_type::{new_atomic_lentype, Atomic}, + storage::{OwnedStorage, Storage, ViewStorage}, + LenType, +}; /// Base struct for [`Queue`] and [`QueueView`], generic over the [`Storage`]. /// /// In most cases you should use [`Queue`] or [`QueueView`] directly. Only use this /// struct if you want to write code that's generic over both. -pub struct QueueInner { - dequeue_pos: AtomicTargetSize, - enqueue_pos: AtomicTargetSize, - buffer: UnsafeCell>>, +pub struct QueueInner { + dequeue_pos: LenT::Atomic, + enqueue_pos: LenT::Atomic, + buffer: UnsafeCell>>, } /// A statically allocated multi-producer, multi-consumer queue with a capacity of `N` elements. @@ -140,17 +129,15 @@ pub struct QueueInner { /// `N` must be a power of 2. /// /// -/// -/// The maximum value of `N` is 128 if the `mpmc_large` feature is not enabled. -pub type Queue = QueueInner>; +pub type Queue = QueueInner>; /// A [`Queue`] with dynamic capacity. /// /// [`Queue`] coerces to `QueueView`. `QueueView` is `!Sized`, meaning it can only ever be used by /// reference. -pub type QueueView = QueueInner; +pub type QueueView = QueueInner; -impl Queue { +impl Queue { #[deprecated( note = "See the documentation of Queue::new() for more information: https://docs.rs/heapless/latest/heapless/mpmc/type.Queue.html#method.new" )] @@ -187,35 +174,44 @@ impl Queue { const { assert!(N > 1); assert!(N.is_power_of_two()); - assert!(N < UintSize::MAX as usize); + assert!(N < LenT::MAX_USIZE); } + let mut buffer: MaybeUninit<[Cell; N]> = MaybeUninit::uninit(); let mut cell_count = 0; - - let mut result_cells: [Cell; N] = [const { Cell::new(0) }; N]; while cell_count != N { - result_cells[cell_count] = Cell::new(cell_count); + // SAFETY: we write to each element exactly once before reading + unsafe { + buffer + .as_mut_ptr() + .cast::>() + .add(cell_count) + .write(Cell::new(cell_count)); + } cell_count += 1; } + // SAFETY: all N elements have been initialized + let buffer = unsafe { buffer.assume_init() }; + Self { - buffer: UnsafeCell::new(result_cells), - dequeue_pos: AtomicTargetSize::new(0), - enqueue_pos: AtomicTargetSize::new(0), + buffer: UnsafeCell::new(buffer), + dequeue_pos: new_atomic_lentype::(0), + enqueue_pos: new_atomic_lentype::(0), } } /// Used in `Storage` implementation. - pub(crate) fn as_view_private(&self) -> &QueueView { + pub(crate) fn as_view_private(&self) -> &QueueView { self } /// Used in `Storage` implementation. - pub(crate) fn as_view_mut_private(&mut self) -> &mut QueueView { + pub(crate) fn as_view_mut_private(&mut self) -> &mut QueueView { self } } -impl QueueInner { +impl QueueInner { /// Returns the maximum number of elements the queue can hold. #[inline] pub fn capacity(&self) -> usize { @@ -240,7 +236,7 @@ impl QueueInner { /// let view: &QueueView = &queue; /// ``` #[inline] - pub fn as_view(&self) -> &QueueView { + pub fn as_view(&self) -> &QueueView { S::as_mpmc_view(self) } @@ -263,12 +259,12 @@ impl QueueInner { /// let view: &mut QueueView = &mut queue; /// ``` #[inline] - pub fn as_mut_view(&mut self) -> &mut QueueView { + pub fn as_mut_view(&mut self) -> &mut QueueView { S::as_mpmc_mut_view(self) } - fn mask(&self) -> UintSize { - (S::len(self.buffer.get()) - 1) as _ + fn mask(&self) -> LenT { + LenT::from_usize(S::len(self.buffer.get()) - 1) } /// Returns the item in the front of the queue, or `None` if the queue is empty. @@ -291,55 +287,55 @@ impl QueueInner { } } -impl Default for Queue { +impl Default for Queue { fn default() -> Self { #[allow(deprecated)] Self::new() } } -impl Drop for QueueInner { +impl Drop for QueueInner { fn drop(&mut self) { // Drop all elements currently in the queue. while self.dequeue().is_some() {} } } -unsafe impl Sync for QueueInner where T: Send {} +unsafe impl Sync for QueueInner where T: Send {} -struct Cell { +struct Cell { data: MaybeUninit, - sequence: AtomicTargetSize, + sequence: LenT::Atomic, } -impl Cell { +impl Cell { const fn new(seq: usize) -> Self { Self { data: MaybeUninit::uninit(), - sequence: AtomicTargetSize::new(seq as UintSize), + sequence: new_atomic_lentype::(seq), } } } -unsafe fn dequeue( - buffer: *mut Cell, - dequeue_pos: &AtomicTargetSize, - mask: UintSize, +unsafe fn dequeue( + buffer: *mut Cell, + dequeue_pos: &LenT::Atomic, + mask: LenT, ) -> Option { let mut pos = dequeue_pos.load(Ordering::Relaxed); let mut cell; loop { - cell = buffer.add(usize::from(pos & mask)); + cell = buffer.add((pos & mask).into_usize()); let seq = (*cell).sequence.load(Ordering::Acquire); - let dif = (seq as IntSize).wrapping_sub((pos.wrapping_add(1)) as IntSize); + let dif = LenT::signed_wrapping_cmp(seq, pos.wrapping_add(LenT::one())); - match dif.cmp(&0) { + match dif { core::cmp::Ordering::Equal => { if dequeue_pos .compare_exchange_weak( pos, - pos.wrapping_add(1), + pos.wrapping_add(LenT::one()), Ordering::Relaxed, Ordering::Relaxed, ) @@ -358,32 +354,33 @@ unsafe fn dequeue( } let data = (*cell).data.as_ptr().read(); - (*cell) - .sequence - .store(pos.wrapping_add(mask).wrapping_add(1), Ordering::Release); + (*cell).sequence.store( + pos.wrapping_add(mask).wrapping_add(LenT::one()), + Ordering::Release, + ); Some(data) } -unsafe fn enqueue( - buffer: *mut Cell, - enqueue_pos: &AtomicTargetSize, - mask: UintSize, +unsafe fn enqueue( + buffer: *mut Cell, + enqueue_pos: &LenT::Atomic, + mask: LenT, item: T, ) -> Result<(), T> { let mut pos = enqueue_pos.load(Ordering::Relaxed); let mut cell; loop { - cell = buffer.add(usize::from(pos & mask)); + cell = buffer.add((pos & mask).into_usize()); let seq = (*cell).sequence.load(Ordering::Acquire); - let dif = (seq as IntSize).wrapping_sub(pos as IntSize); + let dif = LenT::signed_wrapping_cmp(seq, pos); - match dif.cmp(&0) { + match dif { core::cmp::Ordering::Equal => { if enqueue_pos .compare_exchange_weak( pos, - pos.wrapping_add(1), + pos.wrapping_add(LenT::one()), Ordering::Relaxed, Ordering::Relaxed, ) @@ -404,7 +401,7 @@ unsafe fn enqueue( (*cell).data.as_mut_ptr().write(item); (*cell) .sequence - .store(pos.wrapping_add(1), Ordering::Release); + .store(pos.wrapping_add(LenT::one()), Ordering::Release); Ok(()) } @@ -412,7 +409,7 @@ unsafe fn enqueue( mod tests { use static_assertions::assert_not_impl_any; - use super::Queue; + use super::{LenType, Queue}; // Ensure a `Queue` containing `!Send` values stays `!Send` itself. assert_not_impl_any!(Queue<*const (), 4>: Send); @@ -430,10 +427,9 @@ mod tests { assert_eq!(Droppable::count(), 0); } - #[test] - fn sanity() { + fn sanity_len() { #[expect(deprecated)] - let q = Queue::<_, 2>::new(); + let q = Queue::<_, 2, LenT>::new(); q.enqueue(0).unwrap(); q.enqueue(1).unwrap(); assert!(q.enqueue(2).is_err()); @@ -444,23 +440,50 @@ mod tests { } #[test] - fn drain_at_pos255() { + fn sanity() { + sanity_len::(); + } + + #[test] + fn sanity_u16() { + sanity_len::(); + } + + #[test] + fn sanity_u32() { + sanity_len::(); + } + + #[test] + fn sanity_usize() { + sanity_len::(); + } + + fn drain_at_wrap_len() { #[expect(deprecated)] - let q = Queue::<_, 2>::new(); - for _ in 0..255 { + let q = Queue::<_, 2, LenT>::new(); + for _ in 0..LenT::MAX_USIZE { assert!(q.enqueue(0).is_ok()); assert_eq!(q.dequeue(), Some(0)); } - // Queue is empty, this should not block forever. assert_eq!(q.dequeue(), None); } #[test] - fn full_at_wrapped_pos0() { + fn drain_at_pos255() { + drain_at_wrap_len::(); + } + + #[test] + fn drain_at_wrap_u16() { + drain_at_wrap_len::(); + } + + fn full_at_wrapped_pos0_len() { #[expect(deprecated)] - let q = Queue::<_, 2>::new(); - for _ in 0..254 { + let q = Queue::<_, 2, LenT>::new(); + for _ in 0..LenT::MAX_USIZE - 1 { assert!(q.enqueue(0).is_ok()); assert_eq!(q.dequeue(), Some(0)); } @@ -471,23 +494,38 @@ mod tests { } #[test] - fn enqueue_full() { - #[cfg(not(feature = "mpmc_large"))] - const CAPACITY: usize = 128; + fn full_at_wrapped_pos0() { + full_at_wrapped_pos0_len::(); + } - #[cfg(feature = "mpmc_large")] - const CAPACITY: usize = 256; + #[test] + fn full_at_wrapped_pos0_u16() { + full_at_wrapped_pos0_len::(); + } + fn enqueue_full_len() { #[expect(deprecated)] - let q: Queue = Queue::new(); - - assert_eq!(q.capacity(), CAPACITY); - - for _ in 0..CAPACITY { + let q: Queue = Queue::new(); + assert_eq!(q.capacity(), CAP); + for _ in 0..CAP { q.enqueue(0xAA).unwrap(); } - // Queue is full, this should not block forever. q.enqueue(0x55).unwrap_err(); } + + #[test] + fn enqueue_full() { + enqueue_full_len::(); + } + + #[test] + fn enqueue_full_usize() { + enqueue_full_len::(); + } + + #[test] + fn enqueue_full_u16_large_capacity() { + enqueue_full_len::(); + } } diff --git a/src/storage.rs b/src/storage.rs index 4151760e04..cd1c382033 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -9,12 +9,8 @@ use core::borrow::{Borrow, BorrowMut}; ))] use crate::spsc; -#[cfg(any( - feature = "portable-atomic", - all(feature = "mpmc_large", target_has_atomic = "ptr"), - all(not(feature = "mpmc_large"), target_has_atomic = "8") -))] -use crate::mpmc; +#[cfg(any(feature = "portable-atomic", target_has_atomic = "ptr",))] +use crate::{mpmc, LenType}; pub(crate) trait SealedStorage { type Buffer: ?Sized + Borrow<[T]> + BorrowMut<[T]>; @@ -25,20 +21,16 @@ pub(crate) trait SealedStorage { #[allow(unused)] fn as_ptr(this: *mut Self::Buffer) -> *mut T; - #[cfg(any( - feature = "portable-atomic", - all(feature = "mpmc_large", target_has_atomic = "ptr"), - all(not(feature = "mpmc_large"), target_has_atomic = "8") - ))] - fn as_mpmc_view(this: &mpmc::QueueInner) -> &mpmc::QueueView + #[cfg(any(feature = "portable-atomic", target_has_atomic = "ptr",))] + fn as_mpmc_view( + this: &mpmc::QueueInner, + ) -> &mpmc::QueueView where Self: Storage + Sized; - #[cfg(any( - feature = "portable-atomic", - all(feature = "mpmc_large", target_has_atomic = "ptr"), - all(not(feature = "mpmc_large"), target_has_atomic = "8") - ))] - fn as_mpmc_mut_view(this: &mut mpmc::QueueInner) -> &mut mpmc::QueueView + #[cfg(any(feature = "portable-atomic", target_has_atomic = "ptr",))] + fn as_mpmc_mut_view( + this: &mut mpmc::QueueInner, + ) -> &mut mpmc::QueueView where Self: Storage + Sized; @@ -97,24 +89,18 @@ impl SealedStorage for OwnedStorage { fn as_ptr(this: *mut Self::Buffer) -> *mut T { this.cast() } - #[cfg(any( - feature = "portable-atomic", - all(feature = "mpmc_large", target_has_atomic = "ptr"), - all(not(feature = "mpmc_large"), target_has_atomic = "8") - ))] - fn as_mpmc_view(this: &mpmc::Queue) -> &mpmc::QueueView + #[cfg(any(feature = "portable-atomic", target_has_atomic = "ptr",))] + fn as_mpmc_view(this: &mpmc::Queue) -> &mpmc::QueueView where Self: Storage + Sized, { // Fails to compile without the indirection this.as_view_private() } - #[cfg(any( - feature = "portable-atomic", - all(feature = "mpmc_large", target_has_atomic = "ptr"), - all(not(feature = "mpmc_large"), target_has_atomic = "8") - ))] - fn as_mpmc_mut_view(this: &mut mpmc::Queue) -> &mut mpmc::QueueView + #[cfg(any(feature = "portable-atomic", target_has_atomic = "ptr",))] + fn as_mpmc_mut_view( + this: &mut mpmc::Queue, + ) -> &mut mpmc::QueueView where Self: Storage + Sized, { @@ -162,24 +148,20 @@ impl SealedStorage for ViewStorage { this.cast() } - #[cfg(any( - feature = "portable-atomic", - all(feature = "mpmc_large", target_has_atomic = "ptr"), - all(not(feature = "mpmc_large"), target_has_atomic = "8") - ))] - fn as_mpmc_view(this: &mpmc::QueueInner) -> &mpmc::QueueView + #[cfg(any(feature = "portable-atomic", target_has_atomic = "ptr",))] + fn as_mpmc_view( + this: &mpmc::QueueInner, + ) -> &mpmc::QueueView where Self: Storage + Sized, { this } - #[cfg(any( - feature = "portable-atomic", - all(feature = "mpmc_large", target_has_atomic = "ptr"), - all(not(feature = "mpmc_large"), target_has_atomic = "8") - ))] - fn as_mpmc_mut_view(this: &mut mpmc::QueueInner) -> &mut mpmc::QueueView + #[cfg(any(feature = "portable-atomic", target_has_atomic = "ptr",))] + fn as_mpmc_mut_view( + this: &mut mpmc::QueueInner, + ) -> &mut mpmc::QueueView where Self: Storage + Sized, {