use core::sync::atomic::{AtomicBool, AtomicPtr, AtomicU32, AtomicUsize, Ordering};
mod private {
pub trait Sealed {}
}
pub trait AtomicExt: private::Sealed {
type Value;
fn swap_polyfill(&self, val: Self::Value, ordering: Ordering) -> Self::Value;
fn compare_exchange_polyfill(&self, current: Self::Value, new: Self::Value, success: Ordering, failure: Ordering) -> Result<Self::Value, Self::Value>;
fn fetch_update_polyfill(
&self,
set_order: Ordering,
fetch_order: Ordering,
f: impl FnMut(Self::Value) -> Option<Self::Value>
) -> Result<Self::Value, Self::Value>;
}
pub trait AtomicArithExt: AtomicExt {
fn fetch_add_polyfill(&self, val: Self::Value, ordering: Ordering) -> Self::Value;
fn fetch_sub_polyfill(&self, val: Self::Value, ordering: Ordering) -> Self::Value;
fn fetch_or_polyfill(&self, val: Self::Value, ordering: Ordering) -> Self::Value;
}
macro_rules! impl_atomic_polyfills {
($t:ty, $v:ty $(, $param:ident)?) => {
impl<$($param)?> private::Sealed for $t {}
#[cfg(lilos_has_native_rmw)]
impl<$($param)?> AtomicExt for $t {
type Value = $v;
fn swap_polyfill(
&self,
val: Self::Value,
ordering: Ordering,
) -> Self::Value {
self.swap(val, ordering)
}
fn compare_exchange_polyfill(
&self,
current: Self::Value,
new: Self::Value,
success: Ordering,
failure: Ordering,
) -> Result<Self::Value, Self::Value> {
self.compare_exchange(current, new, success, failure)
}
fn fetch_update_polyfill(
&self,
set_order: Ordering,
fetch_order: Ordering,
f: impl FnMut(Self::Value) -> Option<Self::Value>,
) -> Result<Self::Value, Self::Value> {
self.fetch_update(set_order, fetch_order, f)
}
}
#[cfg(not(lilos_has_native_rmw))]
impl<$($param)?> AtomicExt for $t {
type Value = $v;
#[inline(always)]
fn swap_polyfill(
&self,
val: Self::Value,
ordering: Ordering,
) -> Self::Value {
let (lo, so) = rmw_ordering(ordering);
cortex_m::interrupt::free(|_| {
let x = self.load(lo);
self.store(val, so);
x
})
}
fn compare_exchange_polyfill(
&self,
current: Self::Value,
new: Self::Value,
success: Ordering,
failure: Ordering,
) -> Result<Self::Value, Self::Value> {
let (lo, so) = rmw_ordering(success);
cortex_m::interrupt::free(|_| {
let x = self.load(lo);
if x == current {
self.store(new, so);
Ok(x)
} else {
self.store(x, failure);
Err(x)
}
})
}
fn fetch_update_polyfill(
&self,
set_order: Ordering,
fetch_order: Ordering,
mut f: impl FnMut(Self::Value) -> Option<Self::Value>
) -> Result<Self::Value, Self::Value> {
let mut prev = self.load(fetch_order);
while let Some(next) = f(prev) {
let cx_result = self.compare_exchange_polyfill(
prev,
next,
set_order,
fetch_order,
);
match cx_result {
Ok(x) => return Ok(x),
Err(change) => prev = change,
}
}
Err(prev)
}
}
};
}
impl_atomic_polyfills!(AtomicU32, u32);
impl_atomic_polyfills!(AtomicUsize, usize);
impl_atomic_polyfills!(core::sync::atomic::AtomicIsize, isize);
impl_atomic_polyfills!(AtomicBool, bool);
impl_atomic_polyfills!(AtomicPtr<T>, *mut T, T);
macro_rules! impl_atomic_arith_polyfills {
($t:ty $(, $param:ident)?) => {
#[cfg(lilos_has_native_rmw)]
impl<$($param)?> AtomicArithExt for $t {
fn fetch_add_polyfill(
&self,
val: Self::Value,
ordering: Ordering,
) -> Self::Value {
self.fetch_add(val, ordering)
}
fn fetch_sub_polyfill(
&self,
val: Self::Value,
ordering: Ordering,
) -> Self::Value {
self.fetch_sub(val, ordering)
}
fn fetch_or_polyfill(
&self,
val: Self::Value,
ordering: Ordering,
) -> Self::Value {
self.fetch_or(val, ordering)
}
}
#[cfg(not(lilos_has_native_rmw))]
impl<$($param)?> AtomicArithExt for $t {
fn fetch_add_polyfill(
&self,
val: Self::Value,
ordering: Ordering,
) -> Self::Value {
let (lo, so) = rmw_ordering(ordering);
cortex_m::interrupt::free(|_| {
let x = self.load(lo);
self.store(x.wrapping_add(val), so);
x
})
}
fn fetch_sub_polyfill(
&self,
val: Self::Value,
ordering: Ordering,
) -> Self::Value {
let (lo, so) = rmw_ordering(ordering);
cortex_m::interrupt::free(|_| {
let x = self.load(lo);
self.store(x.wrapping_sub(val), so);
x
})
}
fn fetch_or_polyfill(
&self,
val: Self::Value,
ordering: Ordering,
) -> Self::Value {
let (lo, so) = rmw_ordering(ordering);
cortex_m::interrupt::free(|_| {
let x = self.load(lo);
self.store(x | val, so);
x
})
}
}
};
}
impl_atomic_arith_polyfills!(core::sync::atomic::AtomicIsize);
impl_atomic_arith_polyfills!(AtomicUsize);
impl_atomic_arith_polyfills!(AtomicU32);
#[cfg(not(lilos_has_native_rmw))]
#[inline(always)]
fn rmw_ordering(o: Ordering) -> (Ordering, Ordering) {
match o {
Ordering::AcqRel => (Ordering::Acquire, Ordering::Release),
Ordering::Relaxed => (o, o),
Ordering::SeqCst => (o, o),
Ordering::Acquire => (Ordering::Acquire, Ordering::Relaxed),
Ordering::Release => (Ordering::Relaxed, Ordering::Release),
_ => panic!(),
}
}