Skip to main content

soft_cycle/
payload.rs

1//! Atomic payload types and the [`Payload`] trait for use with [`crate::SoftCycleController`].
2
3use std::sync::atomic::{
4    AtomicBool, AtomicI8, AtomicI16, AtomicI32, AtomicI64, AtomicIsize, AtomicPtr, AtomicU8,
5    AtomicU16, AtomicU32, AtomicU64, AtomicUsize, Ordering,
6};
7
8/// Trait for atomic wrapper types used as the storage for controller payloads.
9///
10/// Types implementing this trait support atomic load and store of an inner primitive;
11/// other operations and orderings are not used by the controller. Only payload types
12/// that can be represented by such an atomic type are usable with [`SoftCycleController`](crate::SoftCycleController).
13/// This trait is unrelated to the `AtomicPrimitive` trait in `std::sync::atomic`.
14pub trait AtomicPrimitive: Sync + Send + Sized {
15    /// The primitive type that this atomic type is wrapping.
16    type InnerPrimitive: Default;
17
18    /// Creates a new atomic wrapper with the given value.
19    fn new(value: Self::InnerPrimitive) -> Self;
20    /// Loads the value from the atomic wrapper.
21    fn load(&self) -> Self::InnerPrimitive;
22    /// Stores the value into the atomic wrapper.
23    fn store(&self, value: Self::InnerPrimitive);
24
25    /// Creates a new atomic wrapper with the default value of the primitive
26    /// type.
27    fn new_default() -> Self {
28        Self::new(Default::default())
29    }
30}
31
32macro_rules! impl_atomic_primitive {
33    ($atomic:ty, $inner:ty) => {
34        impl AtomicPrimitive for $atomic {
35            type InnerPrimitive = $inner;
36
37            fn new(value: $inner) -> Self {
38                <$atomic>::new(value)
39            }
40
41            fn load(&self) -> $inner {
42                self.load(Ordering::Relaxed)
43            }
44
45            fn store(&self, value: $inner) {
46                self.store(value, Ordering::Relaxed)
47            }
48        }
49    };
50}
51
52impl_atomic_primitive!(AtomicBool, bool);
53impl_atomic_primitive!(AtomicU8, u8);
54impl_atomic_primitive!(AtomicI8, i8);
55impl_atomic_primitive!(AtomicU16, u16);
56impl_atomic_primitive!(AtomicI16, i16);
57impl_atomic_primitive!(AtomicU32, u32);
58impl_atomic_primitive!(AtomicI32, i32);
59impl_atomic_primitive!(AtomicU64, u64);
60impl_atomic_primitive!(AtomicI64, i64);
61impl_atomic_primitive!(AtomicUsize, usize);
62impl_atomic_primitive!(AtomicIsize, isize);
63
64impl<T> AtomicPrimitive for AtomicPtr<T> {
65    type InnerPrimitive = *mut T;
66
67    fn new(value: *mut T) -> Self {
68        AtomicPtr::new(value)
69    }
70
71    fn load(&self) -> *mut T {
72        self.load(Ordering::Relaxed)
73    }
74
75    fn store(&self, value: *mut T) {
76        self.store(value, Ordering::Relaxed)
77    }
78}
79
80/// Atomic storage for `*const T`. Used as [`Payload::UnderlyingAtomic`] for `*const T`.
81#[derive(Debug, Default)]
82pub struct AtomicConstPtr<T> {
83    p: AtomicPtr<T>,
84}
85
86impl<T> AtomicPrimitive for AtomicConstPtr<T> {
87    type InnerPrimitive = *const T;
88
89    fn new(value: *const T) -> Self {
90        // SAFETY: `p` will never be dereferenced, so casting value to *mut T is
91        // safe.
92        Self {
93            p: AtomicPtr::new(value as *mut T),
94        }
95    }
96
97    fn load(&self) -> *const T {
98        self.p.load(Ordering::Relaxed) as *const T
99    }
100
101    fn store(&self, value: *const T) {
102        // SAFETY: `p` will never be dereferenced, so casting value to *mut T is
103        // safe.
104        self.p.store(value as *mut T, Ordering::Relaxed)
105    }
106}
107
108/// Atomic storage for `()`. Used as [`Payload::UnderlyingAtomic`] for the unit type.
109#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
110pub struct AtomicUnit;
111
112impl AtomicPrimitive for AtomicUnit {
113    type InnerPrimitive = ();
114
115    fn new(_: ()) -> Self {
116        Self
117    }
118
119    fn load(&self) -> Self::InnerPrimitive {}
120
121    fn store(&self, _: Self::InnerPrimitive) {}
122}
123
124/// Trait for payload types that can be stored and loaded atomically by the
125/// [`SoftCycleController`](crate::SoftCycleController).
126///
127/// A type implements `Payload` by specifying an [`UnderlyingAtomic`](Payload::UnderlyingAtomic)
128/// that implements [`AtomicPrimitive`], and implementing `From`/`Into` for the atomic’s inner
129/// type. Only types representable in a single atomic slot (e.g. primitives, pointers) are
130/// supported; the controller does not allocate or clone payloads.
131pub trait Payload:
132    From<<Self::UnderlyingAtomic as AtomicPrimitive>::InnerPrimitive>
133    + Into<<Self::UnderlyingAtomic as AtomicPrimitive>::InnerPrimitive>
134    + Copy
135{
136    type UnderlyingAtomic: AtomicPrimitive;
137}
138
139macro_rules! impl_payload {
140    ($inner:ty, $atomic:ty) => {
141        impl Payload for $inner {
142            type UnderlyingAtomic = $atomic;
143        }
144    };
145}
146
147impl_payload!((), AtomicUnit);
148impl_payload!(bool, AtomicBool);
149impl_payload!(u8, AtomicU8);
150impl_payload!(i8, AtomicI8);
151impl_payload!(u16, AtomicU16);
152impl_payload!(i16, AtomicI16);
153impl_payload!(u32, AtomicU32);
154impl_payload!(i32, AtomicI32);
155impl_payload!(u64, AtomicU64);
156impl_payload!(i64, AtomicI64);
157impl_payload!(usize, AtomicUsize);
158impl_payload!(isize, AtomicIsize);
159
160impl<T> Payload for *mut T {
161    type UnderlyingAtomic = AtomicPtr<T>;
162}
163
164impl<T> Payload for *const T {
165    type UnderlyingAtomic = AtomicConstPtr<T>;
166}