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
11pub struct Scope {
13 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 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
75pub struct ActiveScope;
77
78impl ActiveScope {
79 pub unsafe fn set(scope: &Scope) {
87 ACTIVE_SCOPE_PTR.write_current(scope.ptr.addr().into());
88 }
89
90 pub fn set_global() {
92 ACTIVE_SCOPE_PTR.write_current(0);
93 }
94
95 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}