ax_percpu/value.rs
1use core::{marker::PhantomData, ptr::NonNull};
2
3use cpu_local::{CpuPin, ExclusiveCpu};
4
5use crate::PerCpuArea;
6
7/// Provider generated for one concrete symbol in the per-CPU template.
8///
9/// # Safety
10///
11/// The returned offset must identify a live, properly aligned `T` in every
12/// initialized CPU area. Every returned pointer must address that same `T` in
13/// the selected live area.
14///
15/// Primitive providers must use the matching atomic representation.
16#[doc(hidden)]
17pub unsafe trait PerCpuSymbol<T> {
18 fn offset() -> usize;
19 fn current_ptr(pin: &CpuPin<'_>) -> NonNull<T>;
20 fn remote_ptr(area: PerCpuArea) -> NonNull<T>;
21}
22
23/// Marker implemented only by macro-generated object symbols.
24///
25/// # Safety
26///
27/// The provider's storage must contain a live `T` in every initialized area.
28#[doc(hidden)]
29pub unsafe trait PerCpuObjectSymbol<T>: PerCpuSymbol<T> {}
30
31/// Marker implemented only by macro-generated atomic scalar symbols.
32///
33/// # Safety
34///
35/// The provider's storage must use the atomic representation paired with `T`.
36#[doc(hidden)]
37pub unsafe trait PerCpuPrimitiveSymbol<T>: PerCpuSymbol<T> {}
38
39type PerCpuMarker<T, S> = fn() -> (T, S);
40
41/// Typed descriptor for one symbol replicated in every runtime CPU area.
42pub struct PerCpu<T, S> {
43 _marker: PhantomData<PerCpuMarker<T, S>>,
44}
45
46impl<T, S> PerCpu<T, S>
47where
48 S: PerCpuSymbol<T>,
49{
50 /// Creates the zero-sized descriptor for a macro-generated symbol.
51 #[doc(hidden)]
52 pub const fn new() -> Self {
53 Self {
54 _marker: PhantomData,
55 }
56 }
57
58 /// Returns this symbol's byte offset in one area.
59 pub fn offset(&self) -> usize {
60 S::offset()
61 }
62
63 /// Returns a typed pointer whose address is stable for `pin`.
64 pub fn current_ptr(&self, pin: &CpuPin<'_>) -> NonNull<T> {
65 S::current_ptr(pin)
66 }
67
68 /// Returns a typed pointer in an explicitly selected remote area.
69 ///
70 /// The caller remains responsible for synchronization before dereference.
71 pub fn remote_ptr(&self, area: PerCpuArea) -> NonNull<T> {
72 S::remote_ptr(area)
73 }
74}
75
76impl<T, S> PerCpu<T, S>
77where
78 S: PerCpuObjectSymbol<T>,
79{
80 /// Mutates the current CPU's object without allowing its borrow to escape.
81 pub fn with_current_mut<R>(
82 &self,
83 exclusive: &ExclusiveCpu<'_>,
84 operation: impl for<'value> FnOnce(&'value mut T) -> R,
85 ) -> R {
86 // SAFETY: ExclusiveCpu proves local and remote alias exclusion for the
87 // closure, while its area fixes the generated address.
88 let mut pointer =
89 unsafe { NonNull::new_unchecked((exclusive.area().base() + S::offset()) as *mut T) };
90 operation(unsafe { pointer.as_mut() })
91 }
92
93 /// Mutates the current CPU's object before a [`CpuPin`] exists.
94 ///
95 /// # Errors
96 ///
97 /// Returns [`cpu_local::CpuLocalError::AreaNotInstalled`] before the current
98 /// CPU has installed its runtime area, or an address error for an invalid
99 /// symbol address.
100 ///
101 /// # Safety
102 ///
103 /// The caller must prevent migration, context switches, and local
104 /// IRQ/re-entry for the complete callback, and exclude every conflicting
105 /// remote access to this object. Offline CPU bootstrap satisfies the same
106 /// contract before interrupt publication.
107 #[doc(hidden)]
108 pub unsafe fn with_current_cpu_area_mut<R>(
109 &self,
110 operation: impl for<'value> FnOnce(&'value mut T) -> R,
111 ) -> Result<R, cpu_local::CpuLocalError> {
112 unsafe {
113 cpu_local::with_current_cpu_area(|area| {
114 // SAFETY: the unsafe provider contract guarantees that its
115 // offset addresses a live, aligned T in every CPU area.
116 let mut pointer = area.symbol_ptr::<T>(S::offset())?;
117 Ok(operation(pointer.as_mut()))
118 })?
119 }
120 }
121}
122
123impl<T, S> PerCpu<T, S>
124where
125 T: Sync,
126 S: PerCpuObjectSymbol<T>,
127{
128 /// Borrows a shared current-CPU object for one non-escaping callback.
129 pub fn with_current<R>(
130 &self,
131 pin: &CpuPin<'_>,
132 operation: impl for<'value> FnOnce(&'value T) -> R,
133 ) -> R {
134 // SAFETY: T: Sync permits shared observation and the pin fixes address.
135 operation(unsafe { S::current_ptr(pin).as_ref() })
136 }
137
138 /// Borrows the current CPU's object before a [`CpuPin`] exists.
139 ///
140 /// # Errors
141 ///
142 /// Returns [`cpu_local::CpuLocalError::AreaNotInstalled`] before the current
143 /// CPU has installed its runtime area, or an address error for an invalid
144 /// symbol address.
145 ///
146 /// # Safety
147 ///
148 /// The caller must prevent migration and context switches for the complete
149 /// callback and exclude every conflicting mutation of this object. Offline
150 /// CPU bootstrap satisfies the same contract before interrupt publication.
151 #[doc(hidden)]
152 pub unsafe fn with_current_cpu_area<R>(
153 &self,
154 operation: impl for<'value> FnOnce(&'value T) -> R,
155 ) -> Result<R, cpu_local::CpuLocalError> {
156 unsafe {
157 cpu_local::with_current_cpu_area(|area| {
158 // SAFETY: the unsafe provider contract guarantees that its
159 // offset addresses a live, aligned T in every CPU area.
160 let pointer = area.symbol_ptr::<T>(S::offset())?;
161 Ok(operation(pointer.as_ref()))
162 })?
163 }
164 }
165}
166
167mod primitive {
168 use core::{
169 ptr::NonNull,
170 sync::atomic::{
171 AtomicBool, AtomicU8, AtomicU16, AtomicU32, AtomicU64, AtomicUsize, Ordering,
172 },
173 };
174
175 pub trait Sealed: Copy {
176 unsafe fn load(pointer: NonNull<Self>) -> Self;
177 unsafe fn store(pointer: NonNull<Self>, value: Self);
178 }
179
180 macro_rules! impl_atomic_primitive {
181 ($value:ty, $atomic:ty) => {
182 impl Sealed for $value {
183 unsafe fn load(pointer: NonNull<Self>) -> Self {
184 unsafe { pointer.cast::<$atomic>().as_ref() }.load(Ordering::Relaxed)
185 }
186
187 unsafe fn store(pointer: NonNull<Self>, value: Self) {
188 unsafe { pointer.cast::<$atomic>().as_ref() }.store(value, Ordering::Relaxed);
189 }
190 }
191 };
192 }
193
194 impl_atomic_primitive!(bool, AtomicBool);
195 impl_atomic_primitive!(u8, AtomicU8);
196 impl_atomic_primitive!(u16, AtomicU16);
197 impl_atomic_primitive!(u32, AtomicU32);
198 impl_atomic_primitive!(u64, AtomicU64);
199 impl_atomic_primitive!(usize, AtomicUsize);
200}
201
202impl<T, S> PerCpu<T, S>
203where
204 T: primitive::Sealed,
205 S: PerCpuPrimitiveSymbol<T>,
206{
207 /// Loads the current CPU's atomic scalar with relaxed ordering.
208 pub fn read_current(&self, pin: &CpuPin<'_>) -> T {
209 unsafe { T::load(S::current_ptr(pin)) }
210 }
211
212 /// Stores the current CPU's atomic scalar with relaxed ordering.
213 pub fn write_current(&self, pin: &CpuPin<'_>, value: T) {
214 unsafe { T::store(S::current_ptr(pin), value) }
215 }
216}