1use core::{
2 alloc::Layout,
3 marker::PhantomData,
4 ops::{Deref, DerefMut},
5 ptr::{NonNull, addr_of},
6};
7
8use ax_percpu::CpuPin;
9use ax_sync::PreemptGuard;
10
11use crate::scope::{ActiveScope, Scope};
12
13#[doc(hidden)]
14pub struct Item {
15 pub layout: Layout,
16 pub init: fn(NonNull<()>),
17 pub drop: fn(NonNull<()>),
18}
19
20pub(crate) struct Registry;
21
22impl Deref for Registry {
23 type Target = [Item];
24
25 fn deref(&self) -> &Self::Target {
26 unsafe extern "Rust" {
27 static __start_scope_local: Item;
28 static __stop_scope_local: Item;
29 }
30 let start = addr_of!(__start_scope_local) as usize;
31 let len = (addr_of!(__stop_scope_local) as usize - start) / core::mem::size_of::<Item>();
32 unsafe { core::slice::from_raw_parts(start as *const Item, len) }
33 }
34}
35
36impl Item {
37 #[doc(hidden)]
44 pub const unsafe fn new<T: Send + Sync + 'static>(
45 init: fn(NonNull<()>),
46 drop: fn(NonNull<()>),
47 ) -> Self {
48 Self {
49 layout: Layout::new::<T>(),
50 init,
51 drop,
52 }
53 }
54
55 #[inline]
56 pub(crate) fn index(&'static self) -> usize {
57 unsafe { (self as *const Item).offset_from_unsigned(Registry.as_ptr()) }
58 }
59}
60
61pub struct LocalItem<T> {
63 item: &'static Item,
64 _p: PhantomData<T>,
65}
66
67impl<T: Send + Sync + 'static> LocalItem<T> {
68 #[doc(hidden)]
69 #[inline]
70 pub const unsafe fn new(item: &'static Item) -> Self {
74 Self {
75 item,
76 _p: PhantomData,
77 }
78 }
79
80 pub fn with<R>(&self, operation: impl for<'access> FnOnce(&'access T) -> R) -> R {
87 let mut operation = Some(operation);
88 loop {
89 let guard = PreemptGuard::new();
90 let result = unsafe {
92 ax_percpu::with_cpu_pin(|pin| {
93 ActiveScope::try_with_item(self.item, pin, |item| {
94 let operation = operation
95 .take()
96 .expect("scope-local operation must run at most once");
97 operation(item.as_ref())
98 })
99 })
100 }
101 .expect("scope-local access requires an installed CPU area");
102 drop(guard);
103
104 if let Some(result) = result {
105 return result;
106 }
107 ActiveScope::initialize_global();
108 }
109 }
110
111 pub fn with_pinned<R>(
118 &self,
119 pin: &CpuPin<'_>,
120 operation: impl for<'access> FnOnce(&'access T) -> R,
121 ) -> R {
122 ActiveScope::with_item(self.item, pin, |item| operation(item.as_ref()))
123 }
124
125 pub fn try_with_pinned<R>(
130 &self,
131 pin: &CpuPin<'_>,
132 operation: impl for<'access> FnOnce(&'access T) -> R,
133 ) -> Option<R> {
134 ActiveScope::try_with_item(self.item, pin, |item| operation(item.as_ref()))
135 }
136
137 pub fn clone_current(&self) -> T
139 where
140 T: Clone,
141 {
142 self.with(Clone::clone)
143 }
144
145 pub fn scope<'scope>(&self, scope: &'scope Scope) -> ScopeItem<'scope, T> {
147 ScopeItem {
148 item: self.item,
149 scope,
150 _p: PhantomData,
151 }
152 }
153
154 pub fn scope_mut<'scope>(&self, scope: &'scope mut Scope) -> ScopeItemMut<'scope, T> {
156 ScopeItemMut {
157 item: self.item,
158 scope,
159 _p: PhantomData,
160 }
161 }
162}
163
164pub struct ScopeItem<'scope, T> {
168 item: &'static Item,
169 scope: &'scope Scope,
170 _p: PhantomData<T>,
171}
172
173impl<'scope, T> Deref for ScopeItem<'scope, T> {
174 type Target = T;
175
176 #[inline]
177 fn deref(&self) -> &Self::Target {
178 self.scope.get(self.item).as_ref()
179 }
180}
181
182pub struct ScopeItemMut<'scope, T> {
186 item: &'static Item,
187 scope: &'scope mut Scope,
188 _p: PhantomData<T>,
189}
190
191impl<'scope, T> Deref for ScopeItemMut<'scope, T> {
192 type Target = T;
193
194 #[inline]
195 fn deref(&self) -> &Self::Target {
196 self.scope.get(self.item).as_ref()
197 }
198}
199
200impl<'scope, T> DerefMut for ScopeItemMut<'scope, T> {
201 #[inline]
202 fn deref_mut(&mut self) -> &mut Self::Target {
203 self.scope.get_mut(self.item).as_mut()
204 }
205}
206
207#[macro_export]
222macro_rules! scope_local {
223 ( $( $(#[$attr:meta])* $vis:vis static $name:ident: $ty:ty = $default:expr; )+ ) => {
224 $(
225 $(#[$attr])*
226 $vis static $name: $crate::LocalItem<$ty> = {
227 #[unsafe(link_section = "scope_local")]
228 static ITEM: $crate::Item = unsafe {
229 $crate::Item::new::<$ty>(|ptr| {
230 let val: $ty = $default;
231 ptr.cast().write(val)
232 }, |ptr| {
233 ptr.cast::<$ty>().drop_in_place();
234 })
235 };
236
237 unsafe { $crate::LocalItem::new(&ITEM) }
238 };
239 )+
240 }
241}