#[cfg(all(target_arch = "aarch64", target_os = "none"))]
pub mod aarch64;
use crate::lock::ExceptionLock;
use core::ptr::with_exposed_provenance;
pub use percore_derive::percore;
#[allow(improper_ctypes)]
unsafe extern "Rust" {
#[cfg_attr(
any(
target_os = "none",
target_os = "linux",
target_os = "android",
target_os = "fuchsia",
target_os = "psp",
target_os = "freebsd",
target_os = "openbsd",
),
link_name = "__start_percore"
)]
#[cfg_attr(
any(target_os = "macos", target_os = "ios", target_os = "tvos"),
link_name = "\x01section$start$__DATA$__percore"
)]
pub safe static START_PERCORE: ();
#[cfg_attr(
any(
target_os = "none",
target_os = "linux",
target_os = "android",
target_os = "fuchsia",
target_os = "psp",
target_os = "freebsd",
target_os = "openbsd",
),
link_name = "__stop_percore"
)]
#[cfg_attr(
any(target_os = "macos", target_os = "ios", target_os = "tvos"),
link_name = "\x01section$end$__DATA$__percore"
)]
pub safe static STOP_PERCORE: ();
}
pub fn percore_size() -> usize {
&raw const STOP_PERCORE as usize - &raw const START_PERCORE as usize
}
pub unsafe fn percore_copy_secondary_data(secondary_percore_area: *mut [u8]) {
let percore_start = (&raw const START_PERCORE).cast::<u8>();
let percore_size = percore_size();
if percore_size == 0 {
return;
}
assert!(secondary_percore_area.len().is_multiple_of(percore_size));
let copies = secondary_percore_area.len() / percore_size;
for i in 0..copies {
let dest = (secondary_percore_area as *mut u8).wrapping_byte_add(i * percore_size);
unsafe {
percore_start.copy_to_nonoverlapping(dest, percore_size);
}
}
secondary_percore_area.expose_provenance();
}
pub unsafe trait PercoreLocalOffset {
fn percore_local_offset() -> isize;
}
unsafe extern "Rust" {
safe fn percore_local_offset() -> isize;
}
#[repr(transparent)]
pub struct LinkedPerCore<T>(T);
impl<T> LinkedPerCore<T> {
pub const unsafe fn new(value: T) -> Self {
Self(value)
}
#[inline(always)]
pub fn get(&self) -> &T {
let percore_ptr = with_exposed_provenance::<T>(
((&raw const self.0)
.expose_provenance()
.cast_signed()
.wrapping_add(percore_local_offset()))
.cast_unsigned(),
);
debug_assert!(!percore_ptr.is_null());
debug_assert!(percore_ptr.is_aligned());
unsafe { &*percore_ptr }
}
}
unsafe impl<T: Send> Sync for LinkedPerCore<ExceptionLock<T>> {}
#[macro_export]
macro_rules! percore_local_offset {
($t:ident) => {
#[doc(hidden)]
#[unsafe(export_name = "percore_local_offset")]
fn __percore_local_offset() -> isize {
<$t as $crate::derive::PercoreLocalOffset>::percore_local_offset()
}
};
}
#[cfg(test)]
mod tests {
use super::*;
use crate as percore;
use crate::ExceptionFree;
use core::{cell::RefCell, num::NonZero, ptr::NonNull};
use std::{thread, thread_local};
thread_local! {
static PERCORE_REGION: RefCell<Option<NonNull<[u8]>>> = RefCell::new(None);
}
percore_local_offset!(PercoreLocalOffsetImpl);
struct PercoreLocalOffsetImpl;
unsafe impl PercoreLocalOffset for PercoreLocalOffsetImpl {
fn percore_local_offset() -> isize {
PERCORE_REGION.with_borrow_mut(|region| {
let region = region.get_or_insert_with(|| {
let new_region = Box::into_raw(vec![0; percore_size()].into_boxed_slice());
unsafe {
percore_copy_secondary_data(new_region);
}
NonNull::new(new_region).unwrap()
});
isize::try_from(region.addr().get())
.unwrap()
.checked_sub((&raw const START_PERCORE).addr().try_into().unwrap())
.unwrap()
})
}
}
#[test]
fn test_percore_derive() {
#[percore]
static VALUE: ExceptionLock<RefCell<u64>> =
ExceptionLock::new(RefCell::new(0xabcd_ef01_2345_6789));
let token = unsafe { ExceptionFree::new() };
assert_eq!(*VALUE.get().borrow(token).borrow(), 0xabcd_ef01_2345_6789);
*VALUE.get().borrow_mut(token) = 10;
assert_eq!(*VALUE.get().borrow(token).borrow(), 10);
}
#[test]
fn derive_unsafe() {
#[percore]
static VALUE: ExceptionLock<NonZero<u64>> =
ExceptionLock::new(unsafe { NonZero::new_unchecked(42) });
}
#[test]
fn multiple_cores() {
let token = unsafe { ExceptionFree::new() };
#[percore]
static VALUE: ExceptionLock<RefCell<u64>> = ExceptionLock::new(RefCell::new(42));
assert_eq!(*VALUE.get().borrow_mut(token), 42);
*VALUE.get().borrow_mut(token) = 1;
assert_eq!(*VALUE.get().borrow_mut(token), 1);
thread::spawn(|| {
let token = unsafe { ExceptionFree::new() };
assert_eq!(*VALUE.get().borrow_mut(token), 42);
*VALUE.get().borrow_mut(token) = 2;
assert_eq!(*VALUE.get().borrow_mut(token), 2);
})
.join()
.unwrap();
assert_eq!(*VALUE.get().borrow_mut(token), 1);
}
}