scope_local/
scope.rs

1use alloc::alloc::{alloc, dealloc, handle_alloc_error};
2use core::{alloc::Layout, iter::zip, mem::MaybeUninit, ptr::NonNull};
3
4use spin::Lazy;
5
6use crate::{
7    boxed::ItemBox,
8    item::{Item, Registry},
9};
10
11/// A scope is a collection of items.
12pub struct Scope {
13    // Not using [ItemBox<A>] to save a `usize` because we know the length
14    ptr: NonNull<ItemBox>,
15}
16
17unsafe impl Send for Scope {}
18unsafe impl Sync for Scope {}
19
20impl Scope {
21    fn layout() -> Layout {
22        Layout::array::<ItemBox>(Registry.len()).unwrap()
23    }
24
25    /// Create a new namespace with all resources initialized as their default
26    /// value.
27    pub fn new() -> Self {
28        let layout = Self::layout();
29        let ptr = NonNull::new(unsafe { alloc(layout) })
30            .unwrap_or_else(|| handle_alloc_error(layout))
31            .cast();
32
33        let slice = unsafe {
34            core::slice::from_raw_parts_mut(ptr.cast::<MaybeUninit<_>>().as_ptr(), Registry.len())
35        };
36        for (item, d) in zip(&*Registry, slice) {
37            d.write(ItemBox::new(item));
38        }
39
40        Self { ptr }
41    }
42
43    pub(crate) fn get(&self, item: &'static Item) -> &ItemBox {
44        let index = item.index();
45        unsafe { self.ptr.add(index).as_ref() }
46    }
47
48    pub(crate) fn get_mut(&mut self, item: &'static Item) -> &mut ItemBox {
49        let index = item.index();
50        unsafe { self.ptr.add(index).as_mut() }
51    }
52}
53
54impl Default for Scope {
55    fn default() -> Self {
56        Self::new()
57    }
58}
59
60impl Drop for Scope {
61    fn drop(&mut self) {
62        let ptr = NonNull::slice_from_raw_parts(self.ptr, Registry.len());
63        unsafe {
64            ptr.drop_in_place();
65            dealloc(self.ptr.cast().as_ptr(), Self::layout());
66        }
67    }
68}
69
70static GLOBAL_SCOPE: Lazy<Scope> = Lazy::new(Scope::new);
71
72#[percpu::def_percpu]
73pub(crate) static ACTIVE_SCOPE_PTR: usize = 0;
74
75/// Currently active scope.
76pub struct ActiveScope;
77
78impl ActiveScope {
79    /// Sets the active scope pointer to the given scope.
80    ///
81    /// # Safety
82    ///
83    /// The caller must ensure that the provided `scope` reference is valid for
84    /// the duration in which it is set as the active scope, and that no data
85    /// races or aliasing violations occur.
86    pub unsafe fn set(scope: &Scope) {
87        ACTIVE_SCOPE_PTR.write_current(scope.ptr.addr().into());
88    }
89
90    /// Set the active scope to the global scope.
91    pub fn set_global() {
92        ACTIVE_SCOPE_PTR.write_current(0);
93    }
94
95    /// Returns true if the active scope is the global scope.
96    pub fn is_global() -> bool {
97        ACTIVE_SCOPE_PTR.read_current() == 0
98    }
99
100    pub(crate) fn get<'a>(item: &'static Item) -> &'a ItemBox {
101        let ptr = ACTIVE_SCOPE_PTR.read_current();
102        let ptr = NonNull::new(ptr as _).unwrap_or(GLOBAL_SCOPE.ptr);
103        let index = item.index();
104        unsafe { ptr.add(index).as_ref() }
105    }
106}