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