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