use core::{
alloc::Layout,
marker::PhantomData,
ops::{Deref, DerefMut},
ptr::{NonNull, addr_of},
};
use ax_kernel_guard::NoPreempt;
use ax_percpu::CpuPin;
use crate::scope::{ActiveScope, Scope};
#[doc(hidden)]
pub struct Item {
pub layout: Layout,
pub init: fn(NonNull<()>),
pub drop: fn(NonNull<()>),
}
pub(crate) struct Registry;
impl Deref for Registry {
type Target = [Item];
fn deref(&self) -> &Self::Target {
unsafe extern "Rust" {
static __start_scope_local: Item;
static __stop_scope_local: Item;
}
let start = addr_of!(__start_scope_local) as usize;
let len = (addr_of!(__stop_scope_local) as usize - start) / core::mem::size_of::<Item>();
unsafe { core::slice::from_raw_parts(start as *const Item, len) }
}
}
impl Item {
#[doc(hidden)]
pub const unsafe fn new<T: Send + Sync + 'static>(
init: fn(NonNull<()>),
drop: fn(NonNull<()>),
) -> Self {
Self {
layout: Layout::new::<T>(),
init,
drop,
}
}
#[inline]
pub(crate) fn index(&'static self) -> usize {
unsafe { (self as *const Item).offset_from_unsigned(Registry.as_ptr()) }
}
}
pub struct LocalItem<T> {
item: &'static Item,
_p: PhantomData<T>,
}
impl<T: Send + Sync + 'static> LocalItem<T> {
#[doc(hidden)]
#[inline]
pub const unsafe fn new(item: &'static Item) -> Self {
Self {
item,
_p: PhantomData,
}
}
pub fn with<R>(&self, operation: impl for<'access> FnOnce(&'access T) -> R) -> R {
let mut operation = Some(operation);
loop {
let guard = NoPreempt::new();
let result = unsafe {
ax_percpu::with_cpu_pin(|pin| {
ActiveScope::try_with_item(self.item, pin, |item| {
let operation = operation
.take()
.expect("scope-local operation must run at most once");
operation(item.as_ref())
})
})
}
.expect("scope-local access requires an installed CPU area");
drop(guard);
if let Some(result) = result {
return result;
}
ActiveScope::initialize_global();
}
}
pub fn with_pinned<R>(
&self,
pin: &CpuPin<'_>,
operation: impl for<'access> FnOnce(&'access T) -> R,
) -> R {
ActiveScope::with_item(self.item, pin, |item| operation(item.as_ref()))
}
pub fn try_with_pinned<R>(
&self,
pin: &CpuPin<'_>,
operation: impl for<'access> FnOnce(&'access T) -> R,
) -> Option<R> {
ActiveScope::try_with_item(self.item, pin, |item| operation(item.as_ref()))
}
pub fn clone_current(&self) -> T
where
T: Clone,
{
self.with(Clone::clone)
}
pub fn scope<'scope>(&self, scope: &'scope Scope) -> ScopeItem<'scope, T> {
ScopeItem {
item: self.item,
scope,
_p: PhantomData,
}
}
pub fn scope_mut<'scope>(&self, scope: &'scope mut Scope) -> ScopeItemMut<'scope, T> {
ScopeItemMut {
item: self.item,
scope,
_p: PhantomData,
}
}
}
pub struct ScopeItem<'scope, T> {
item: &'static Item,
scope: &'scope Scope,
_p: PhantomData<T>,
}
impl<'scope, T> Deref for ScopeItem<'scope, T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
self.scope.get(self.item).as_ref()
}
}
pub struct ScopeItemMut<'scope, T> {
item: &'static Item,
scope: &'scope mut Scope,
_p: PhantomData<T>,
}
impl<'scope, T> Deref for ScopeItemMut<'scope, T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
self.scope.get(self.item).as_ref()
}
}
impl<'scope, T> DerefMut for ScopeItemMut<'scope, T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
self.scope.get_mut(self.item).as_mut()
}
}
#[macro_export]
macro_rules! scope_local {
( $( $(#[$attr:meta])* $vis:vis static $name:ident: $ty:ty = $default:expr; )+ ) => {
$(
$(#[$attr])*
$vis static $name: $crate::LocalItem<$ty> = {
#[unsafe(link_section = "scope_local")]
static ITEM: $crate::Item = unsafe {
$crate::Item::new::<$ty>(|ptr| {
let val: $ty = $default;
ptr.cast().write(val)
}, |ptr| {
ptr.cast::<$ty>().drop_in_place();
})
};
unsafe { $crate::LocalItem::new(&ITEM) }
};
)+
}
}