1*2387fb2aSBoqun Feng // SPDX-License-Identifier: GPL-2.0 2*2387fb2aSBoqun Feng 3*2387fb2aSBoqun Feng //! Atomic internal implementations. 4*2387fb2aSBoqun Feng //! 5*2387fb2aSBoqun Feng //! Provides 1:1 mapping to the C atomic operations. 6*2387fb2aSBoqun Feng 7*2387fb2aSBoqun Feng use crate::bindings; 8*2387fb2aSBoqun Feng use crate::macros::paste; 9*2387fb2aSBoqun Feng use core::cell::UnsafeCell; 10*2387fb2aSBoqun Feng 11*2387fb2aSBoqun Feng mod private { 12*2387fb2aSBoqun Feng /// Sealed trait marker to disable customized impls on atomic implementation traits. 13*2387fb2aSBoqun Feng pub trait Sealed {} 14*2387fb2aSBoqun Feng } 15*2387fb2aSBoqun Feng 16*2387fb2aSBoqun Feng // `i32` and `i64` are only supported atomic implementations. 17*2387fb2aSBoqun Feng impl private::Sealed for i32 {} 18*2387fb2aSBoqun Feng impl private::Sealed for i64 {} 19*2387fb2aSBoqun Feng 20*2387fb2aSBoqun Feng /// A marker trait for types that implement atomic operations with C side primitives. 21*2387fb2aSBoqun Feng /// 22*2387fb2aSBoqun Feng /// This trait is sealed, and only types that have directly mapping to the C side atomics should 23*2387fb2aSBoqun Feng /// impl this: 24*2387fb2aSBoqun Feng /// 25*2387fb2aSBoqun Feng /// - `i32` maps to `atomic_t`. 26*2387fb2aSBoqun Feng /// - `i64` maps to `atomic64_t`. 27*2387fb2aSBoqun Feng pub trait AtomicImpl: Sized + Send + Copy + private::Sealed { 28*2387fb2aSBoqun Feng /// The type of the delta in arithmetic or logical operations. 29*2387fb2aSBoqun Feng /// 30*2387fb2aSBoqun Feng /// For example, in `atomic_add(ptr, v)`, it's the type of `v`. Usually it's the same type of 31*2387fb2aSBoqun Feng /// [`Self`], but it may be different for the atomic pointer type. 32*2387fb2aSBoqun Feng type Delta; 33*2387fb2aSBoqun Feng } 34*2387fb2aSBoqun Feng 35*2387fb2aSBoqun Feng // `atomic_t` implements atomic operations on `i32`. 36*2387fb2aSBoqun Feng impl AtomicImpl for i32 { 37*2387fb2aSBoqun Feng type Delta = Self; 38*2387fb2aSBoqun Feng } 39*2387fb2aSBoqun Feng 40*2387fb2aSBoqun Feng // `atomic64_t` implements atomic operations on `i64`. 41*2387fb2aSBoqun Feng impl AtomicImpl for i64 { 42*2387fb2aSBoqun Feng type Delta = Self; 43*2387fb2aSBoqun Feng } 44*2387fb2aSBoqun Feng 45*2387fb2aSBoqun Feng /// Atomic representation. 46*2387fb2aSBoqun Feng #[repr(transparent)] 47*2387fb2aSBoqun Feng pub struct AtomicRepr<T: AtomicImpl>(UnsafeCell<T>); 48*2387fb2aSBoqun Feng 49*2387fb2aSBoqun Feng impl<T: AtomicImpl> AtomicRepr<T> { 50*2387fb2aSBoqun Feng /// Creates a new atomic representation `T`. 51*2387fb2aSBoqun Feng pub const fn new(v: T) -> Self { 52*2387fb2aSBoqun Feng Self(UnsafeCell::new(v)) 53*2387fb2aSBoqun Feng } 54*2387fb2aSBoqun Feng 55*2387fb2aSBoqun Feng /// Returns a pointer to the underlying `T`. 56*2387fb2aSBoqun Feng /// 57*2387fb2aSBoqun Feng /// # Guarantees 58*2387fb2aSBoqun Feng /// 59*2387fb2aSBoqun Feng /// The returned pointer is valid and properly aligned (i.e. aligned to [`align_of::<T>()`]). 60*2387fb2aSBoqun Feng pub const fn as_ptr(&self) -> *mut T { 61*2387fb2aSBoqun Feng // GUARANTEE: `self.0` is an `UnsafeCell<T>`, therefore the pointer returned by `.get()` 62*2387fb2aSBoqun Feng // must be valid and properly aligned. 63*2387fb2aSBoqun Feng self.0.get() 64*2387fb2aSBoqun Feng } 65*2387fb2aSBoqun Feng } 66*2387fb2aSBoqun Feng 67*2387fb2aSBoqun Feng // This macro generates the function signature with given argument list and return type. 68*2387fb2aSBoqun Feng macro_rules! declare_atomic_method { 69*2387fb2aSBoqun Feng ( 70*2387fb2aSBoqun Feng $(#[doc=$doc:expr])* 71*2387fb2aSBoqun Feng $func:ident($($arg:ident : $arg_type:ty),*) $(-> $ret:ty)? 72*2387fb2aSBoqun Feng ) => { 73*2387fb2aSBoqun Feng paste!( 74*2387fb2aSBoqun Feng $(#[doc = $doc])* 75*2387fb2aSBoqun Feng fn [< atomic_ $func >]($($arg: $arg_type,)*) $(-> $ret)?; 76*2387fb2aSBoqun Feng ); 77*2387fb2aSBoqun Feng }; 78*2387fb2aSBoqun Feng ( 79*2387fb2aSBoqun Feng $(#[doc=$doc:expr])* 80*2387fb2aSBoqun Feng $func:ident [$variant:ident $($rest:ident)*]($($arg_sig:tt)*) $(-> $ret:ty)? 81*2387fb2aSBoqun Feng ) => { 82*2387fb2aSBoqun Feng paste!( 83*2387fb2aSBoqun Feng declare_atomic_method!( 84*2387fb2aSBoqun Feng $(#[doc = $doc])* 85*2387fb2aSBoqun Feng [< $func _ $variant >]($($arg_sig)*) $(-> $ret)? 86*2387fb2aSBoqun Feng ); 87*2387fb2aSBoqun Feng ); 88*2387fb2aSBoqun Feng 89*2387fb2aSBoqun Feng declare_atomic_method!( 90*2387fb2aSBoqun Feng $(#[doc = $doc])* 91*2387fb2aSBoqun Feng $func [$($rest)*]($($arg_sig)*) $(-> $ret)? 92*2387fb2aSBoqun Feng ); 93*2387fb2aSBoqun Feng }; 94*2387fb2aSBoqun Feng ( 95*2387fb2aSBoqun Feng $(#[doc=$doc:expr])* 96*2387fb2aSBoqun Feng $func:ident []($($arg_sig:tt)*) $(-> $ret:ty)? 97*2387fb2aSBoqun Feng ) => { 98*2387fb2aSBoqun Feng declare_atomic_method!( 99*2387fb2aSBoqun Feng $(#[doc = $doc])* 100*2387fb2aSBoqun Feng $func($($arg_sig)*) $(-> $ret)? 101*2387fb2aSBoqun Feng ); 102*2387fb2aSBoqun Feng } 103*2387fb2aSBoqun Feng } 104*2387fb2aSBoqun Feng 105*2387fb2aSBoqun Feng // This macro generates the function implementation with given argument list and return type, and it 106*2387fb2aSBoqun Feng // will replace "call(...)" expression with "$ctype _ $func" to call the real C function. 107*2387fb2aSBoqun Feng macro_rules! impl_atomic_method { 108*2387fb2aSBoqun Feng ( 109*2387fb2aSBoqun Feng ($ctype:ident) $func:ident($($arg:ident: $arg_type:ty),*) $(-> $ret:ty)? { 110*2387fb2aSBoqun Feng $unsafe:tt { call($($c_arg:expr),*) } 111*2387fb2aSBoqun Feng } 112*2387fb2aSBoqun Feng ) => { 113*2387fb2aSBoqun Feng paste!( 114*2387fb2aSBoqun Feng #[inline(always)] 115*2387fb2aSBoqun Feng fn [< atomic_ $func >]($($arg: $arg_type,)*) $(-> $ret)? { 116*2387fb2aSBoqun Feng // TODO: Ideally we want to use the SAFETY comments written at the macro invocation 117*2387fb2aSBoqun Feng // (e.g. in `declare_and_impl_atomic_methods!()`, however, since SAFETY comments 118*2387fb2aSBoqun Feng // are just comments, and they are not passed to macros as tokens, therefore we 119*2387fb2aSBoqun Feng // cannot use them here. One potential improvement is that if we support using 120*2387fb2aSBoqun Feng // attributes as an alternative for SAFETY comments, then we can use that for macro 121*2387fb2aSBoqun Feng // generating code. 122*2387fb2aSBoqun Feng // 123*2387fb2aSBoqun Feng // SAFETY: specified on macro invocation. 124*2387fb2aSBoqun Feng $unsafe { bindings::[< $ctype _ $func >]($($c_arg,)*) } 125*2387fb2aSBoqun Feng } 126*2387fb2aSBoqun Feng ); 127*2387fb2aSBoqun Feng }; 128*2387fb2aSBoqun Feng ( 129*2387fb2aSBoqun Feng ($ctype:ident) $func:ident[$variant:ident $($rest:ident)*]($($arg_sig:tt)*) $(-> $ret:ty)? { 130*2387fb2aSBoqun Feng $unsafe:tt { call($($arg:tt)*) } 131*2387fb2aSBoqun Feng } 132*2387fb2aSBoqun Feng ) => { 133*2387fb2aSBoqun Feng paste!( 134*2387fb2aSBoqun Feng impl_atomic_method!( 135*2387fb2aSBoqun Feng ($ctype) [< $func _ $variant >]($($arg_sig)*) $( -> $ret)? { 136*2387fb2aSBoqun Feng $unsafe { call($($arg)*) } 137*2387fb2aSBoqun Feng } 138*2387fb2aSBoqun Feng ); 139*2387fb2aSBoqun Feng ); 140*2387fb2aSBoqun Feng impl_atomic_method!( 141*2387fb2aSBoqun Feng ($ctype) $func [$($rest)*]($($arg_sig)*) $( -> $ret)? { 142*2387fb2aSBoqun Feng $unsafe { call($($arg)*) } 143*2387fb2aSBoqun Feng } 144*2387fb2aSBoqun Feng ); 145*2387fb2aSBoqun Feng }; 146*2387fb2aSBoqun Feng ( 147*2387fb2aSBoqun Feng ($ctype:ident) $func:ident[]($($arg_sig:tt)*) $( -> $ret:ty)? { 148*2387fb2aSBoqun Feng $unsafe:tt { call($($arg:tt)*) } 149*2387fb2aSBoqun Feng } 150*2387fb2aSBoqun Feng ) => { 151*2387fb2aSBoqun Feng impl_atomic_method!( 152*2387fb2aSBoqun Feng ($ctype) $func($($arg_sig)*) $(-> $ret)? { 153*2387fb2aSBoqun Feng $unsafe { call($($arg)*) } 154*2387fb2aSBoqun Feng } 155*2387fb2aSBoqun Feng ); 156*2387fb2aSBoqun Feng } 157*2387fb2aSBoqun Feng } 158*2387fb2aSBoqun Feng 159*2387fb2aSBoqun Feng // Delcares $ops trait with methods and implements the trait for `i32` and `i64`. 160*2387fb2aSBoqun Feng macro_rules! declare_and_impl_atomic_methods { 161*2387fb2aSBoqun Feng ($(#[$attr:meta])* $pub:vis trait $ops:ident { 162*2387fb2aSBoqun Feng $( 163*2387fb2aSBoqun Feng $(#[doc=$doc:expr])* 164*2387fb2aSBoqun Feng fn $func:ident [$($variant:ident),*]($($arg_sig:tt)*) $( -> $ret:ty)? { 165*2387fb2aSBoqun Feng $unsafe:tt { bindings::#call($($arg:tt)*) } 166*2387fb2aSBoqun Feng } 167*2387fb2aSBoqun Feng )* 168*2387fb2aSBoqun Feng }) => { 169*2387fb2aSBoqun Feng $(#[$attr])* 170*2387fb2aSBoqun Feng $pub trait $ops: AtomicImpl { 171*2387fb2aSBoqun Feng $( 172*2387fb2aSBoqun Feng declare_atomic_method!( 173*2387fb2aSBoqun Feng $(#[doc=$doc])* 174*2387fb2aSBoqun Feng $func[$($variant)*]($($arg_sig)*) $(-> $ret)? 175*2387fb2aSBoqun Feng ); 176*2387fb2aSBoqun Feng )* 177*2387fb2aSBoqun Feng } 178*2387fb2aSBoqun Feng 179*2387fb2aSBoqun Feng impl $ops for i32 { 180*2387fb2aSBoqun Feng $( 181*2387fb2aSBoqun Feng impl_atomic_method!( 182*2387fb2aSBoqun Feng (atomic) $func[$($variant)*]($($arg_sig)*) $(-> $ret)? { 183*2387fb2aSBoqun Feng $unsafe { call($($arg)*) } 184*2387fb2aSBoqun Feng } 185*2387fb2aSBoqun Feng ); 186*2387fb2aSBoqun Feng )* 187*2387fb2aSBoqun Feng } 188*2387fb2aSBoqun Feng 189*2387fb2aSBoqun Feng impl $ops for i64 { 190*2387fb2aSBoqun Feng $( 191*2387fb2aSBoqun Feng impl_atomic_method!( 192*2387fb2aSBoqun Feng (atomic64) $func[$($variant)*]($($arg_sig)*) $(-> $ret)? { 193*2387fb2aSBoqun Feng $unsafe { call($($arg)*) } 194*2387fb2aSBoqun Feng } 195*2387fb2aSBoqun Feng ); 196*2387fb2aSBoqun Feng )* 197*2387fb2aSBoqun Feng } 198*2387fb2aSBoqun Feng } 199*2387fb2aSBoqun Feng } 200*2387fb2aSBoqun Feng 201*2387fb2aSBoqun Feng declare_and_impl_atomic_methods!( 202*2387fb2aSBoqun Feng /// Basic atomic operations 203*2387fb2aSBoqun Feng pub trait AtomicBasicOps { 204*2387fb2aSBoqun Feng /// Atomic read (load). 205*2387fb2aSBoqun Feng fn read[acquire](a: &AtomicRepr<Self>) -> Self { 206*2387fb2aSBoqun Feng // SAFETY: `a.as_ptr()` is valid and properly aligned. 207*2387fb2aSBoqun Feng unsafe { bindings::#call(a.as_ptr().cast()) } 208*2387fb2aSBoqun Feng } 209*2387fb2aSBoqun Feng 210*2387fb2aSBoqun Feng /// Atomic set (store). 211*2387fb2aSBoqun Feng fn set[release](a: &AtomicRepr<Self>, v: Self) { 212*2387fb2aSBoqun Feng // SAFETY: `a.as_ptr()` is valid and properly aligned. 213*2387fb2aSBoqun Feng unsafe { bindings::#call(a.as_ptr().cast(), v) } 214*2387fb2aSBoqun Feng } 215*2387fb2aSBoqun Feng } 216*2387fb2aSBoqun Feng ); 217*2387fb2aSBoqun Feng 218*2387fb2aSBoqun Feng declare_and_impl_atomic_methods!( 219*2387fb2aSBoqun Feng /// Exchange and compare-and-exchange atomic operations 220*2387fb2aSBoqun Feng pub trait AtomicExchangeOps { 221*2387fb2aSBoqun Feng /// Atomic exchange. 222*2387fb2aSBoqun Feng /// 223*2387fb2aSBoqun Feng /// Atomically updates `*a` to `v` and returns the old value. 224*2387fb2aSBoqun Feng fn xchg[acquire, release, relaxed](a: &AtomicRepr<Self>, v: Self) -> Self { 225*2387fb2aSBoqun Feng // SAFETY: `a.as_ptr()` is valid and properly aligned. 226*2387fb2aSBoqun Feng unsafe { bindings::#call(a.as_ptr().cast(), v) } 227*2387fb2aSBoqun Feng } 228*2387fb2aSBoqun Feng 229*2387fb2aSBoqun Feng /// Atomic compare and exchange. 230*2387fb2aSBoqun Feng /// 231*2387fb2aSBoqun Feng /// If `*a` == `*old`, atomically updates `*a` to `new`. Otherwise, `*a` is not 232*2387fb2aSBoqun Feng /// modified, `*old` is updated to the current value of `*a`. 233*2387fb2aSBoqun Feng /// 234*2387fb2aSBoqun Feng /// Return `true` if the update of `*a` occurred, `false` otherwise. 235*2387fb2aSBoqun Feng fn try_cmpxchg[acquire, release, relaxed]( 236*2387fb2aSBoqun Feng a: &AtomicRepr<Self>, old: &mut Self, new: Self 237*2387fb2aSBoqun Feng ) -> bool { 238*2387fb2aSBoqun Feng // SAFETY: `a.as_ptr()` is valid and properly aligned. `core::ptr::from_mut(old)` 239*2387fb2aSBoqun Feng // is valid and properly aligned. 240*2387fb2aSBoqun Feng unsafe { bindings::#call(a.as_ptr().cast(), core::ptr::from_mut(old), new) } 241*2387fb2aSBoqun Feng } 242*2387fb2aSBoqun Feng } 243*2387fb2aSBoqun Feng ); 244*2387fb2aSBoqun Feng 245*2387fb2aSBoqun Feng declare_and_impl_atomic_methods!( 246*2387fb2aSBoqun Feng /// Atomic arithmetic operations 247*2387fb2aSBoqun Feng pub trait AtomicArithmeticOps { 248*2387fb2aSBoqun Feng /// Atomic add (wrapping). 249*2387fb2aSBoqun Feng /// 250*2387fb2aSBoqun Feng /// Atomically updates `*a` to `(*a).wrapping_add(v)`. 251*2387fb2aSBoqun Feng fn add[](a: &AtomicRepr<Self>, v: Self::Delta) { 252*2387fb2aSBoqun Feng // SAFETY: `a.as_ptr()` is valid and properly aligned. 253*2387fb2aSBoqun Feng unsafe { bindings::#call(v, a.as_ptr().cast()) } 254*2387fb2aSBoqun Feng } 255*2387fb2aSBoqun Feng 256*2387fb2aSBoqun Feng /// Atomic fetch and add (wrapping). 257*2387fb2aSBoqun Feng /// 258*2387fb2aSBoqun Feng /// Atomically updates `*a` to `(*a).wrapping_add(v)`, and returns the value of `*a` 259*2387fb2aSBoqun Feng /// before the update. 260*2387fb2aSBoqun Feng fn fetch_add[acquire, release, relaxed](a: &AtomicRepr<Self>, v: Self::Delta) -> Self { 261*2387fb2aSBoqun Feng // SAFETY: `a.as_ptr()` is valid and properly aligned. 262*2387fb2aSBoqun Feng unsafe { bindings::#call(v, a.as_ptr().cast()) } 263*2387fb2aSBoqun Feng } 264*2387fb2aSBoqun Feng } 265*2387fb2aSBoqun Feng ); 266