percore/lib.rs
1// Copyright 2024 The percore Authors.
2// This project is dual-licensed under Apache 2.0 and MIT terms.
3// See LICENSE-APACHE and LICENSE-MIT for details.
4
5//! Safe per-CPU core mutable state on no_std platforms through exception masking.
6//!
7//! This crate provides two main wrapper types: [`PerCore`] to provide an instance of a value per
8//! CPU core, where each core can access only its instance, and [`ExceptionLock`] to guard a value
9//! so that it can only be accessed while exceptions are masked. These may be combined with
10//! `RefCell` to provide safe per-core mutable state.
11//!
12//! `ExceptionLock` may also be combined with a spinlock-based mutex (such as one provided by the
13//! [`spin`](https://crates.io/crates/spin) crate) to avoid deadlocks when accessing global mutable
14//! state from exception handlers.
15//!
16//! # Example
17//!
18//! ```
19//! use core::cell::RefCell;
20//! # #[cfg(target_arch = "aarch64")]
21//! use percore::{exception_free, Cores, ExceptionLock, PerCore};
22//! # #[cfg(not(target_arch = "aarch64"))]
23//! # use percore::{Cores, ExceptionLock, PerCore};
24//!
25//! /// The total number of CPU cores in the target system.
26//! const CORE_COUNT: usize = 2;
27//!
28//! struct CoresImpl;
29//!
30//! unsafe impl Cores for CoresImpl {
31//! fn core_index() -> usize {
32//! todo!("Return the index of the current CPU core, 0 or 1")
33//! }
34//! }
35//!
36//! struct CoreState {
37//! // Your per-core mutable state goes here...
38//! foo: u32,
39//! }
40//!
41//! const EMPTY_CORE_STATE: ExceptionLock<RefCell<CoreState>> =
42//! ExceptionLock::new(RefCell::new(CoreState { foo: 0 }));
43//! static CORE_STATE: PerCore<ExceptionLock<RefCell<CoreState>>, CoresImpl, CORE_COUNT> =
44//! PerCore::new([EMPTY_CORE_STATE; CORE_COUNT]);
45//!
46//! fn main() {
47//! // Mask exceptions while accessing mutable state.
48//! # #[cfg(target_arch = "aarch64")]
49//! exception_free(|token| {
50//! // `token` proves that interrupts are masked, so we can safely access per-core mutable
51//! // state.
52//! CORE_STATE.get().borrow_mut(token).foo = 42;
53//! });
54//! }
55//! ```
56
57#![no_std]
58
59mod exceptions;
60mod lock;
61
62#[cfg(target_arch = "aarch64")]
63pub use self::exceptions::exception_free;
64pub use self::{exceptions::ExceptionFree, lock::ExceptionLock};
65
66use core::marker::PhantomData;
67
68/// Trait abstracting how to get the index of the current CPU core.
69///
70/// # Safety
71///
72/// `core_index` must never return the same index on different CPU cores.
73pub unsafe trait Cores {
74 /// Returns the index of the current CPU core.
75 fn core_index() -> usize;
76}
77
78/// A type which allows values to be stored per CPU core. Only the value associated with the current
79/// CPU core can be accessed.
80///
81/// To use this type you must first implement the [`Cores`] trait for your platform.
82///
83/// `C::core_index()` must always return a value less than `CORE_COUNT` or there will be a runtime
84/// panic.
85pub struct PerCore<T, C: Cores, const CORE_COUNT: usize> {
86 values: [T; CORE_COUNT],
87 _cores: PhantomData<C>,
88}
89
90impl<T, C: Cores, const CORE_COUNT: usize> PerCore<T, C, CORE_COUNT> {
91 /// Creates a new set of per-core values.
92 pub const fn new(values: [T; CORE_COUNT]) -> Self {
93 Self {
94 values,
95 _cores: PhantomData,
96 }
97 }
98
99 /// Gets a shared reference to the value for the current CPU core.
100 pub fn get(&self) -> &T {
101 &self.values[C::core_index()]
102 }
103}
104
105// SAFETY: Both different CPU cores and different exception contexts must be treated as separate
106// 'threads' for the purposes of Rust's memory model. `PerCore` only allows access to the value for
107// the current core, and `ExceptionLock` requires exceptions to be disabled while accessing it which
108// prevents concurrent access to its contents from different exception contexts. The combination of
109// the two therefore prevents concurrent access to `T`.
110unsafe impl<T: Send, C: Cores, const CORE_COUNT: usize> Sync
111 for PerCore<ExceptionLock<T>, C, CORE_COUNT>
112{
113}