blob: 24f5cd6d718ce238119bd6b63c1edfcef19d61c4 [file] [log] [blame]
// Copyright 2024 The percore Authors.
// This project is dual-licensed under Apache 2.0 and MIT terms.
// See LICENSE-APACHE and LICENSE-MIT for details.
//! Safe per-CPU core mutable state on no_std platforms through exception masking.
//!
//! This crate provides two main wrapper types: [`PerCore`] to provide an instance of a value per
//! CPU core, where each core can access only its instance, and [`ExceptionLock`] to guard a value
//! so that it can only be accessed while exceptions are masked. These may be combined with
//! `RefCell` to provide safe per-core mutable state.
//!
//! `ExceptionLock` may also be combined with a spinlock-based mutex (such as one provided by the
//! [`spin`](https://crates.io/crates/spin) crate) to avoid deadlocks when accessing global mutable
//! state from exception handlers.
//!
//! # Example
//!
//! ```
//! use core::cell::RefCell;
//! # #[cfg(target_arch = "aarch64")]
//! use percore::{exception_free, Cores, ExceptionLock, PerCore};
//! # #[cfg(not(target_arch = "aarch64"))]
//! # use percore::{Cores, ExceptionLock, PerCore};
//!
//! /// The total number of CPU cores in the target system.
//! const CORE_COUNT: usize = 2;
//!
//! struct CoresImpl;
//!
//! unsafe impl Cores for CoresImpl {
//! fn core_index() -> usize {
//! todo!("Return the index of the current CPU core, 0 or 1")
//! }
//! }
//!
//! struct CoreState {
//! // Your per-core mutable state goes here...
//! foo: u32,
//! }
//!
//! const EMPTY_CORE_STATE: ExceptionLock<RefCell<CoreState>> =
//! ExceptionLock::new(RefCell::new(CoreState { foo: 0 }));
//! static CORE_STATE: PerCore<ExceptionLock<RefCell<CoreState>>, CoresImpl, CORE_COUNT> =
//! PerCore::new([EMPTY_CORE_STATE; CORE_COUNT]);
//!
//! fn main() {
//! // Mask exceptions while accessing mutable state.
//! # #[cfg(target_arch = "aarch64")]
//! exception_free(|token| {
//! // `token` proves that interrupts are masked, so we can safely access per-core mutable
//! // state.
//! CORE_STATE.get().borrow_mut(token).foo = 42;
//! });
//! }
//! ```
#![no_std]
mod exceptions;
mod lock;
#[cfg(target_arch = "aarch64")]
pub use self::exceptions::exception_free;
pub use self::{exceptions::ExceptionFree, lock::ExceptionLock};
use core::marker::PhantomData;
/// Trait abstracting how to get the index of the current CPU core.
///
/// # Safety
///
/// `core_index` must never return the same index on different CPU cores.
pub unsafe trait Cores {
/// Returns the index of the current CPU core.
fn core_index() -> usize;
}
/// A type which allows values to be stored per CPU core. Only the value associated with the current
/// CPU core can be accessed.
///
/// To use this type you must first implement the [`Cores`] trait for your platform.
///
/// `C::core_index()` must always return a value less than `CORE_COUNT` or there will be a runtime
/// panic.
pub struct PerCore<T, C: Cores, const CORE_COUNT: usize> {
values: [T; CORE_COUNT],
_cores: PhantomData<C>,
}
impl<T, C: Cores, const CORE_COUNT: usize> PerCore<T, C, CORE_COUNT> {
/// Creates a new set of per-core values.
pub const fn new(values: [T; CORE_COUNT]) -> Self {
Self {
values,
_cores: PhantomData,
}
}
/// Gets a shared reference to the value for the current CPU core.
pub fn get(&self) -> &T {
&self.values[C::core_index()]
}
}
// SAFETY: Both different CPU cores and different exception contexts must be treated as separate
// 'threads' for the purposes of Rust's memory model. `PerCore` only allows access to the value for
// the current core, and `ExceptionLock` requires exceptions to be disabled while accessing it which
// prevents concurrent access to its contents from different exception contexts. The combination of
// the two therefore prevents concurrent access to `T`.
unsafe impl<T: Send, C: Cores, const CORE_COUNT: usize> Sync
for PerCore<ExceptionLock<T>, C, CORE_COUNT>
{
}