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}