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
52impl AxNamespace {
53    /// Returns the base address of the namespace, which points to the start of
54    /// all resources.
55    pub const fn base(&self) -> *mut u8 {
56        self.base
57    }
58
59    /// Returns the size of the namespace (size of all resources).
60    pub fn size(&self) -> usize {
61        Self::section_size()
62    }
63
64    /// Returns the size of the `axns_resource` section.
65    fn section_size() -> usize {
66        __stop_axns_resource as usize - __start_axns_resource as usize
67    }
68
69    /// Returns the global namespace.
70    pub fn global() -> Self {
71        Self {
72            base: __start_axns_resource as *mut u8,
73            alloc: false,
74        }
75    }
76
77    /// Constructs a new thread-local namespace.
78    ///
79    /// Each thread can have its own namespace instead of the global one, to
80    /// isolate resources between threads.
81    ///
82    /// This function allocates a memory area to store the thread-local resources,
83    /// and copies from the global namespace as the initial value.
84    #[cfg(feature = "thread-local")]
85    pub fn new_thread_local() -> Self {
86        let size = Self::section_size();
87        let base = if size == 0 {
88            core::ptr::null_mut()
89        } else {
90            let layout = Layout::from_size_align(size, 64).unwrap();
91            let dst = unsafe { alloc::alloc::alloc(layout) };
92            let src = __start_axns_resource as *const u8;
93            unsafe { core::ptr::copy_nonoverlapping(src, dst, size) };
94            dst
95        };
96        Self { base, alloc: true }
97    }
98}
99
100impl Drop for AxNamespace {
101    fn drop(&mut self) {
102        if self.alloc {
103            let size = Self::section_size();
104            if size != 0 && !self.base.is_null() {
105                let layout = Layout::from_size_align(size, 64).unwrap();
106                unsafe { alloc::alloc::dealloc(self.base, layout) };
107            }
108        }
109    }
110}
111
112/// A helper type to easily manage shared resources.
113///
114/// It provides methods to lazily initialize the resource of the current thread,
115/// or to share the resource with other threads.
116pub struct ResArc<T>(LazyInit<Arc<T>>);
117
118impl<T> ResArc<T> {
119    /// Creates a new uninitialized resource.
120    pub const fn new() -> Self {
121        Self(LazyInit::new())
122    }
123
124    /// Returns a shared reference to the resource.
125    pub fn share(&self) -> Arc<T> {
126        self.0.deref().clone()
127    }
128
129    /// Initializes the resource and does not share with others.
130    pub fn init_new(&self, data: T) {
131        self.0.init_once(Arc::new(data));
132    }
133
134    /// Initializes the resource with the shared data.
135    pub fn init_shared(&self, data: Arc<T>) {
136        self.0.init_once(data);
137    }
138
139    /// Checks whether the value is initialized.
140    pub fn is_inited(&self) -> bool {
141        self.0.is_inited()
142    }
143}
144
145impl<T> Deref for ResArc<T> {
146    type Target = T;
147
148    fn deref(&self) -> &Self::Target {
149        self.0.deref()
150    }
151}
152
153impl<T: fmt::Debug> fmt::Debug for ResArc<T> {
154    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
155        self.0.fmt(f)
156    }
157}
158
159/// The interfaces need to be implemented when enable thread-local namespaces.
160#[cfg(feature = "thread-local")]
161#[crate_interface::def_interface]
162pub trait AxNamespaceIf {
163    /// Returns the pointer to the current namespace.
164    ///
165    /// It usually needs to be obtained from the thread local storage.
166    fn current_namespace_base() -> *mut u8;
167}
168
169/// Returns the pointer to the current namespace.
170///
171/// When `thread-local` feature is enabled, it returns the thread-local namespace
172/// of the current thread. Otherwise, it returns the global namespace.
173///
174/// # Safety
175///
176/// This function is unsafe, the returned pointer should not outlive the current
177/// thread.
178pub unsafe fn current_namespace_base() -> *mut u8 {
179    #[cfg(feature = "thread-local")]
180    {
181        crate_interface::call_interface!(AxNamespaceIf::current_namespace_base)
182    }
183    #[cfg(not(feature = "thread-local"))]
184    {
185        AxNamespace::global().base()
186    }
187}
188
189/// Defines a resource that managed by [`AxNamespace`].
190///
191/// Each resource will be collected into the `axns_resource` section. When
192/// accessed, it is either dereferenced from the global namespace or the
193/// thread-local namespace according to the `thread-local` feature.
194///
195/// # Example
196///
197/// ```
198/// use axns::ResArc;
199///
200/// axns::def_resource! {
201///     static FOO: u32 = 42;
202///     static BAR: ResArc<String> = ResArc::new();
203/// }
204///
205/// BAR.init_new("hello world".to_string());
206/// assert_eq!(*FOO, 42);
207/// assert_eq!(BAR.as_str(), "hello world");
208///
209/// mod imp {
210///     use axns::{AxNamespace, AxNamespaceIf};
211///
212///     struct ResArcImpl;
213///
214///     #[crate_interface::impl_interface]
215///     impl AxNamespaceIf for ResArcImpl {
216///         fn current_namespace_base() -> *mut u8 {
217///             AxNamespace::global().base()
218///         }
219///     }
220/// }
221/// ```
222#[macro_export]
223macro_rules! def_resource {
224    ( $( $(#[$attr:meta])* $vis:vis static $name:ident: $ty:ty = $default:expr; )+ ) => {
225        $(
226            #[doc = concat!("Wrapper struct for the namespace resource [`", stringify!($name), "`]")]
227            #[allow(non_camel_case_types)]
228            $vis struct $name { __value: () }
229
230            impl $name {
231                unsafe fn deref_from_base(&self, ns_base: *mut u8) -> &$ty {
232                    unsafe extern {
233                        fn __start_axns_resource();
234                    }
235
236                    #[unsafe(link_section = "axns_resource")]
237                    static RES: $ty = $default;
238
239                    let offset = &RES as *const _ as usize - __start_axns_resource as usize;
240                    let ptr = unsafe{ ns_base.add(offset) } as *const _;
241                    unsafe{ &*ptr }
242                }
243
244                /// Dereference the resource from the given namespace.
245                pub fn deref_from(&self, ns: &$crate::AxNamespace) -> &$ty {
246                    unsafe { self.deref_from_base(ns.base()) }
247                }
248
249                /// Dereference the resource from the global namespace.
250                pub fn deref_global(&self) -> &$ty {
251                    self.deref_from(&$crate::AxNamespace::global())
252                }
253
254                /// Dereference the resource automatically, according whether the
255                /// `thread-local` feature of the `axns` crate is enabled or not.
256                ///
257                /// When the feature is enabled, it dereferences from the
258                /// thread-local namespace of the current thread. Otherwise, it
259                /// dereferences from the global namespace.
260                pub fn deref_auto(&self) -> &$ty {
261                    unsafe { self.deref_from_base($crate::current_namespace_base()) }
262                }
263            }
264
265            impl core::ops::Deref for $name {
266                type Target = $ty;
267
268                #[inline(never)]
269                fn deref(&self) -> &Self::Target {
270                    self.deref_auto()
271                }
272            }
273
274            #[used]
275            #[doc(hidden)]
276            $(#[$attr])*
277            $vis static $name: $name = $name { __value: () };
278        )+
279    };
280}