axns/
lib.rs

1//! [ArceOS](https://github.com/arceos-org/arceos) namespaces module.
2//!
3//! Namespaces are used to control system resource sharing between threads. This
4//! module provides a unified interface to access system resources in different
5//! scenarios.
6//!
7//! For a unikernel, there is only one global namespace, so all threads share
8//! the same system resources, such as virtual address space, working directory,
9//! and file descriptors, etc.
10//!
11//! For a monolithic kernel, each process corresponds to a namespace, all
12//! threads in the same process share the same system resources. Different
13//! processes have different namespaces and isolated resources.
14//!
15//! For further container support, some global system resources can also be
16//! grouped into a namespace.
17//!
18//! See the examples of [`def_resource!`] for more usage.
19
20#![cfg_attr(not(test), no_std)]
21
22extern crate alloc;
23
24use alloc::sync::Arc;
25use core::{alloc::Layout, fmt, ops::Deref};
26
27use lazyinit::LazyInit;
28
29unsafe extern "C" {
30    fn __start_axns_resource();
31    fn __stop_axns_resource();
32}
33
34/// A namespace that contains all user-defined resources.
35///
36/// There are two types of namespaces:
37///
38/// - Global namespace: this namespace is globally unique and all threads share
39///   the resources in it. Resources are statically collected into the
40///   `axns_resource` section, and the global namespace is constructed by the base
41///   address of the section ([`AxNamespace::global`]).
42/// - Thread-local namespace: this namespace is per-thread, each thread should
43///   call [`AxNamespace::new_thread_local()`] to allocate a memory area as its
44///   namespace. Layout of resources in global and thread-local namespaces is
45///   consistent. Each namespace has its own resources, which may be unique or
46///   shared between threads by the [`Arc`] wrapper.
47pub struct AxNamespace {
48    base: *mut u8,
49    alloc: bool,
50}
51
52unsafe impl Send for AxNamespace {}
53unsafe impl Sync for AxNamespace {}
54
55impl AxNamespace {
56    /// Returns the base address of the namespace, which points to the start of
57    /// all resources.
58    pub const fn base(&self) -> *mut u8 {
59        self.base
60    }
61
62    /// Returns the size of the namespace (size of all resources).
63    pub fn size(&self) -> usize {
64        Self::section_size()
65    }
66
67    /// Returns the size of the `axns_resource` section.
68    fn section_size() -> usize {
69        __stop_axns_resource as usize - __start_axns_resource as usize
70    }
71
72    /// Returns the global namespace.
73    pub fn global() -> Self {
74        Self {
75            base: __start_axns_resource as *mut u8,
76            alloc: false,
77        }
78    }
79
80    /// Constructs a new thread-local namespace.
81    ///
82    /// Each thread can have its own namespace instead of the global one, to
83    /// isolate resources between threads.
84    ///
85    /// This function allocates a memory area to store the thread-local resources,
86    /// and copies from the global namespace as the initial value.
87    #[cfg(feature = "thread-local")]
88    pub fn new_thread_local() -> Self {
89        let size = Self::section_size();
90        let base = if size == 0 {
91            core::ptr::null_mut()
92        } else {
93            let layout = Layout::from_size_align(size, 64).unwrap();
94            let dst = unsafe { alloc::alloc::alloc(layout) };
95            let src = __start_axns_resource as *const u8;
96            unsafe { core::ptr::copy_nonoverlapping(src, dst, size) };
97            dst
98        };
99        Self { base, alloc: true }
100    }
101}
102
103impl Drop for AxNamespace {
104    fn drop(&mut self) {
105        if self.alloc {
106            let size = Self::section_size();
107            let base = self.base();
108            if size != 0 && !base.is_null() {
109                let layout = Layout::from_size_align(size, 64).unwrap();
110                unsafe { alloc::alloc::dealloc(base, layout) };
111            }
112        }
113    }
114}
115
116/// A helper type to easily manage shared resources.
117///
118/// It provides methods to lazily initialize the resource of the current thread,
119/// or to share the resource with other threads.
120pub struct ResArc<T>(LazyInit<Arc<T>>);
121
122impl<T> ResArc<T> {
123    /// Creates a new uninitialized resource.
124    pub const fn new() -> Self {
125        Self(LazyInit::new())
126    }
127
128    /// Returns a shared reference to the resource.
129    pub fn share(&self) -> Arc<T> {
130        self.0.deref().clone()
131    }
132
133    /// Initializes the resource and does not share with others.
134    pub fn init_new(&self, data: T) {
135        self.0.init_once(Arc::new(data));
136    }
137
138    /// Initializes the resource with the shared data.
139    pub fn init_shared(&self, data: Arc<T>) {
140        self.0.init_once(data);
141    }
142
143    /// Checks whether the value is initialized.
144    pub fn is_inited(&self) -> bool {
145        self.0.is_inited()
146    }
147}
148
149impl<T> Deref for ResArc<T> {
150    type Target = T;
151
152    fn deref(&self) -> &Self::Target {
153        self.0.deref()
154    }
155}
156
157impl<T: fmt::Debug> fmt::Debug for ResArc<T> {
158    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
159        self.0.fmt(f)
160    }
161}
162
163/// The interfaces need to be implemented when enable thread-local namespaces.
164#[cfg(feature = "thread-local")]
165#[crate_interface::def_interface]
166pub trait AxNamespaceIf {
167    /// Returns the pointer to the current namespace.
168    ///
169    /// It usually needs to be obtained from the thread local storage.
170    fn current_namespace_base() -> *mut u8;
171}
172
173/// Returns the pointer to the current namespace.
174///
175/// When `thread-local` feature is enabled, it returns the thread-local namespace
176/// of the current thread. Otherwise, it returns the global namespace.
177///
178/// # Safety
179///
180/// This function is unsafe, the returned pointer should not outlive the current
181/// thread.
182pub unsafe fn current_namespace_base() -> *mut u8 {
183    #[cfg(feature = "thread-local")]
184    {
185        crate_interface::call_interface!(AxNamespaceIf::current_namespace_base)
186    }
187    #[cfg(not(feature = "thread-local"))]
188    {
189        AxNamespace::global().base()
190    }
191}
192
193/// Defines a resource that managed by [`AxNamespace`].
194///
195/// Each resource will be collected into the `axns_resource` section. When
196/// accessed, it is either dereferenced from the global namespace or the
197/// thread-local namespace according to the `thread-local` feature.
198///
199/// # Example
200///
201/// ```
202/// use axns::ResArc;
203///
204/// axns::def_resource! {
205///     static FOO: u32 = 42;
206///     static BAR: ResArc<String> = ResArc::new();
207/// }
208///
209/// BAR.init_new("hello world".to_string());
210/// assert_eq!(*FOO, 42);
211/// assert_eq!(BAR.as_str(), "hello world");
212///
213/// mod imp {
214///     use axns::{AxNamespace, AxNamespaceIf};
215///
216///     struct ResArcImpl;
217///
218///     #[crate_interface::impl_interface]
219///     impl AxNamespaceIf for ResArcImpl {
220///         fn current_namespace_base() -> *mut u8 {
221///             AxNamespace::global().base()
222///         }
223///     }
224/// }
225/// ```
226#[macro_export]
227macro_rules! def_resource {
228    ( $( $(#[$attr:meta])* $vis:vis static $name:ident: $ty:ty = $default:expr; )+ ) => {
229        $(
230            #[doc = concat!("Wrapper struct for the namespace resource [`", stringify!($name), "`]")]
231            #[allow(non_camel_case_types)]
232            $vis struct $name { __value: () }
233
234            impl $name {
235                unsafe fn deref_from_base(&self, ns_base: *mut u8) -> &$ty {
236                    unsafe extern {
237                        fn __start_axns_resource();
238                    }
239
240                    #[unsafe(link_section = "axns_resource")]
241                    static RES: $ty = $default;
242
243                    let offset = &RES as *const _ as usize - __start_axns_resource as usize;
244                    let ptr = unsafe{ ns_base.add(offset) } as *const _;
245                    unsafe{ &*ptr }
246                }
247
248                /// Dereference the resource from the given namespace.
249                pub fn deref_from(&self, ns: &$crate::AxNamespace) -> &$ty {
250                    unsafe { self.deref_from_base(ns.base()) }
251                }
252
253                /// Dereference the resource from the global namespace.
254                pub fn deref_global(&self) -> &$ty {
255                    self.deref_from(&$crate::AxNamespace::global())
256                }
257
258                /// Dereference the resource automatically, according whether the
259                /// `thread-local` feature of the `axns` crate is enabled or not.
260                ///
261                /// When the feature is enabled, it dereferences from the
262                /// thread-local namespace of the current thread. Otherwise, it
263                /// dereferences from the global namespace.
264                pub fn deref_auto(&self) -> &$ty {
265                    unsafe { self.deref_from_base($crate::current_namespace_base()) }
266                }
267            }
268
269            impl core::ops::Deref for $name {
270                type Target = $ty;
271
272                #[inline(never)]
273                fn deref(&self) -> &Self::Target {
274                    self.deref_auto()
275                }
276            }
277
278            #[used]
279            #[doc(hidden)]
280            $(#[$attr])*
281            $vis static $name: $name = $name { __value: () };
282        )+
283    };
284}