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}