| // Copyright 2025 The Pigweed Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| // use this file except in compliance with the License. You may obtain a copy of |
| // the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| // License for the specific language governing permissions and limitations under |
| // the License. |
| #![no_std] |
| #![cfg_attr(test, no_main)] |
| |
| use core::cell::UnsafeCell; |
| use core::marker::PhantomData; |
| use core::mem::{ManuallyDrop, offset_of}; |
| use core::ops::{Deref, DerefMut}; |
| use core::ptr::NonNull; |
| use core::sync::atomic::Ordering; |
| |
| use pw_atomic::AtomicUsize; |
| |
| pub struct ForeignBox<T: ?Sized> { |
| inner: NonNull<T>, |
| |
| // Guards against the type being dropped w/o a consume call. |
| // |
| // TODO: add feature to disable this field on production builds to avoid |
| // the overhead of the extra bool. |
| consumed: bool, |
| |
| // NunNull is covariant over `T` which can not be guaranteed of `KernelRef`. |
| // Use `PhantomData` in mark as invariant. |
| _phantom: PhantomData<T>, |
| } |
| |
| // Safety: Same bounds on `T` as [`Box`]. |
| unsafe impl<T: Send + ?Sized> Send for ForeignBox<T> {} |
| unsafe impl<T: Sync + ?Sized> Sync for ForeignBox<T> {} |
| |
| impl<T: ?Sized> ForeignBox<T> { |
| #[allow(dead_code)] |
| /// Create a new `ForeignBox` from a `NonNull<T>`. |
| /// |
| /// # Safety |
| /// The caller guarantees that `ptr` remains valid throughout the lifetime |
| /// of the `ForeignBox` object. |
| #[must_use] |
| pub const unsafe fn new(ptr: NonNull<T>) -> Self { |
| Self { |
| inner: ptr, |
| consumed: false, |
| _phantom: PhantomData, |
| } |
| } |
| |
| /// Create a new `ForeignBox` from a `*mut T`. |
| /// |
| /// # Panics |
| /// panics if `ptr` is null. |
| /// # Safety |
| /// The caller guarantees that `ptr` remains valid throughout the lifetime |
| /// of the `ForeignBox` object. |
| #[must_use] |
| pub unsafe fn new_from_ptr(ptr: *mut T) -> Self { |
| let Some(ptr) = NonNull::new(ptr) else { |
| pw_assert::panic!("Null pointer"); |
| }; |
| unsafe { Self::new(ptr) } |
| } |
| |
| #[allow(clippy::must_use_candidate)] |
| pub fn consume(mut self) -> NonNull<T> { |
| self.consumed = true; |
| self.inner |
| } |
| |
| /// Returns a pointer to the contained data. |
| /// |
| /// # Safety |
| /// Creates an "unenforceable borrow" of the contained data. |
| #[must_use] |
| pub unsafe fn as_ptr(&self) -> *const T { |
| self.inner.as_ptr() |
| } |
| |
| /// Returns a mutable pointer to the contained data. |
| /// |
| /// # Safety |
| /// Creates an "mutable unenforceable borrow" of the contained data. |
| #[must_use] |
| pub unsafe fn as_mut_ptr(&mut self) -> *mut T { |
| unsafe { self.inner.as_mut() } |
| } |
| } |
| |
| impl<T: ?Sized> Drop for ForeignBox<T> { |
| fn drop(&mut self) { |
| if !self.consumed { |
| pw_assert::panic!( |
| "ForeignBox@{:08x} dropped before being consumed!", |
| self.inner.as_ptr().cast::<()>() as usize |
| ); |
| } |
| } |
| } |
| |
| impl<T: ?Sized> From<&'static mut T> for ForeignBox<T> { |
| fn from(t: &'static mut T) -> ForeignBox<T> { |
| // SAFETY: The `'static` lifetime guarantees that `t`'s referent will |
| // remain valid for the lifetime of the returned `ForeignBox`. |
| unsafe { ForeignBox::new(NonNull::from(t)) } |
| } |
| } |
| |
| impl<T: ?Sized> AsRef<T> for ForeignBox<T> { |
| fn as_ref(&self) -> &T { |
| unsafe { self.inner.as_ref() } |
| } |
| } |
| |
| impl<T: ?Sized> AsMut<T> for ForeignBox<T> { |
| fn as_mut(&mut self) -> &mut T { |
| unsafe { self.inner.as_mut() } |
| } |
| } |
| |
| impl<T: ?Sized> Deref for ForeignBox<T> { |
| type Target = T; |
| |
| fn deref(&self) -> &Self::Target { |
| self.as_ref() |
| } |
| } |
| |
| impl<T: ?Sized> DerefMut for ForeignBox<T> { |
| fn deref_mut(&mut self) -> &mut Self::Target { |
| self.as_mut() |
| } |
| } |
| |
| #[doc(hidden)] |
| pub struct ForeignRcState<A: AtomicUsize, T: ?Sized> { |
| // Disable auto `Send` and `Sync`. We implement them manually to give us an |
| // opportunity to document their safety proofs. |
| _not_send_sync: PhantomData<NonNull<()>>, |
| |
| // `ForeignRcState` has the following lifecycle: |
| // - At creation, `ref_count` is equal to 1 and no `ForeignRc` references |
| // exist. |
| // - `create_first_ref` is called, which creates the first `ForeignRc` |
| // reference without incrementing the `ref_count`. |
| // - From here on out, `ref_count` counts the number of `ForeignRc` |
| // references which exist or which have been forgotten. Each time a |
| // `ForeignRc` is created or dropped, `ref_count` is updated accordingly. |
| // - Once `ref_count` reaches 0, there are no `ForeignRc` references, and so |
| // `inner` can be dropped. At this point, `inner` is in an invalid state |
| // and must never be operated on or exposed to safe code outside of this |
| // module. |
| // |
| // INVARIANTS: |
| // - `ref_count` is only modified consistent with this lifecycle |
| // - `ref_count` is only modified using thread-safe operations |
| ref_count: A, |
| // INVARIANTS: `inner` is in a valid state so long as `ref_count > 0`. |
| inner: UnsafeCell<ManuallyDrop<T>>, |
| } |
| |
| // SAFETY: None of the `ForeignRcState` lifecycle is affected by sending a |
| // `ForeignRcState` across threads. TODO: Make this more precise |
| unsafe impl<A: Send + AtomicUsize, T: Send + ?Sized> Send for ForeignRcState<A, T> {} |
| // SAFETY: By invariant on `ref_count`, it is only modified using thread-safe |
| // operations. Thus, permitting references (and, by extension, `ForeignRc`s, |
| // which contain a `&ForeignRcState`) to be sent between threads won't violate |
| // the `ForeignRcState` lifecycle (so long as `A: Sync` and `T: Sync`). |
| unsafe impl<A: Sync + AtomicUsize, T: Sync + ?Sized> Sync for ForeignRcState<A, T> {} |
| |
| impl<A: AtomicUsize, T> ForeignRcState<A, T> { |
| pub fn new(val: T) -> Self { |
| Self { |
| ref_count: A::new(1), |
| inner: UnsafeCell::new(ManuallyDrop::new(val)), |
| _not_send_sync: PhantomData, |
| } |
| } |
| } |
| |
| impl<A: AtomicUsize, T: ?Sized> ForeignRcState<A, T> { |
| /// # Safety |
| /// |
| /// `create_first_ref` must be the first method called on `self` after |
| /// creation with [`new`]. It must not be called twice. |
| /// |
| /// [`new`]: ForeignRcState::new |
| pub unsafe fn create_first_ref(&'static mut self) -> ForeignRc<A, T> { |
| // SAFETY: The caller promises that this is the first method call. Thus, |
| // this is the first `ForeignRc` reference, consistent with the |
| // lifecycle. |
| ForeignRc { state: self } |
| } |
| |
| pub fn ref_count(&self) -> &A { |
| &self.ref_count |
| } |
| } |
| |
| impl<A: AtomicUsize, T: Sized> ForeignRcState<A, T> { |
| /// Return a new [`ForeignRc`] from a reference to a [`ForeignRcState`]'s |
| /// inner storage. |
| /// |
| /// # Safety |
| /// The caller must guarantee that `inner: &T` is a reference storage that |
| /// is contained inside a [`ForeignRcState<A, T>`]. |
| pub unsafe fn create_ref_from_inner(inner: &T) -> ForeignRc<A, T> { |
| let inner = NonNull::from_ref(inner); |
| let state: &Self = unsafe { inner.byte_sub(offset_of!(Self, inner)).cast().as_ref() }; |
| state.ref_count.fetch_add(1, Ordering::SeqCst); |
| ForeignRc { state } |
| } |
| } |
| |
| pub struct ForeignRc<A: AtomicUsize + 'static, T: ?Sized + 'static> { |
| state: &'static ForeignRcState<A, T>, |
| } |
| |
| impl<A: AtomicUsize, T: ?Sized> ForeignRc<A, T> { |
| #[must_use] |
| pub fn state(&self) -> &ForeignRcState<A, T> { |
| self.state |
| } |
| |
| #[doc(hidden)] |
| /// Map a `ForeignRc` into a compatible type. |
| /// |
| /// # Safety |
| /// The caller must guarantee that the `map` closure returns a reference |
| /// pointing to the same address and byte range as was passed in. |
| pub unsafe fn map<U: ?Sized>( |
| self, |
| map: impl Fn(&ForeignRcState<A, T>) -> &ForeignRcState<A, U>, |
| ) -> ForeignRc<A, U> { |
| // Ensure the destructor is not run to avoid decrementing the refcount. |
| let this = core::mem::ManuallyDrop::new(self); |
| ForeignRc { |
| state: map(this.state), |
| } |
| } |
| } |
| |
| impl<A: AtomicUsize, T: ?Sized> Deref for ForeignRc<A, T> { |
| type Target = T; |
| |
| fn deref(&self) -> &T { |
| let ptr = self.state.inner.get(); |
| // SAFETY: By invariant, `self.state.ref_count` is at least 1 since this |
| // `ForeignRc` exists, and also by invariant, `self.state.inner` is |
| // valid since `ref_count` is at least 1. |
| unsafe { &*ptr } |
| } |
| } |
| |
| impl<A: AtomicUsize, T: ?Sized> Clone for ForeignRc<A, T> { |
| fn clone(&self) -> Self { |
| self.state.ref_count.fetch_add(1, Ordering::SeqCst); |
| // SAFETY: We're creating a new `ForeignRc`, but we've just increased |
| // the `ref_count` by 1. Since, by invariant, `ref_count` previously |
| // accurately counted the number of `ForeignRc`s, and since we just |
| // created a new one and incremented `ref_count`, it is still accurate. |
| Self { state: self.state } |
| } |
| } |
| |
| impl<A: AtomicUsize, T: ?Sized> Drop for ForeignRc<A, T> { |
| fn drop(&mut self) { |
| let old = self.state.ref_count.fetch_sub(1, Ordering::SeqCst); |
| if old == 1 { |
| let ptr = self.state.inner.get(); |
| // SAFETY: By invariant, `ref_count` counts the number of |
| // `ForeignRc`s which exist or which have been forgotten. Since it |
| // was 1 before being decremented, this is the last `ForeignRc`. It |
| // is sound to construct a mutable reference to `self.state.inner` |
| // because: |
| // - By virtue of being the last `ForeignRc`, no other code is |
| // accessing `self.state.inner` |
| // - By invariant, `self.state.inner` is valid so long as `ref_count |
| // > 0`, which it was at the beginning of this function |
| let md = unsafe { &mut *ptr }; |
| // SAFETY: As described above, `md` references a valid value, so it |
| // is sound to drop it. By invariant, since `ref_count` is now 0, no |
| // other code will be permitted to access `self.state.inner`, so it |
| // is acceptable to leave it in an invalid state. |
| unsafe { ManuallyDrop::drop(md) }; |
| } |
| } |
| } |
| |
| /// Upcasts a [`ForeignRc`] from a concrete type to a `dyn Trait` that the concrete |
| /// type implements |
| #[macro_export] |
| macro_rules! upcast_foreign_rc { |
| ($rc:expr => dyn $trait:ident $(<$($trait_tyvar:ident),*>)? ) => {{ |
| use $crate::ForeignRcState; |
| let rc = $rc; |
| // SAFETY: The closure passed to `.map()` fulfils the precondition that |
| // the returned reference points to the same place as the passed in reference. |
| unsafe { |
| rc.map(|inner: &ForeignRcState<_, _>| |
| -> &ForeignRcState<_, dyn $trait $(<$($trait_tyvar),*>)?> { inner }) |
| } |
| }}; |
| } |
| |
| /// Helper type to declare a static value with runtime initialization. |
| /// |
| /// # Safety |
| /// The user must ensure that [`StaticStorage::init()`] is only called once. |
| #[doc(hidden)] |
| pub struct StaticStorage<T> { |
| inner: UnsafeCell<core::mem::MaybeUninit<T>>, |
| } |
| |
| impl<T> StaticStorage<T> { |
| /// Initialize a new `StaticStorage`. |
| #[must_use] |
| pub const fn new() -> Self { |
| Self { |
| inner: UnsafeCell::new(core::mem::MaybeUninit::uninit()), |
| } |
| } |
| |
| /// Initialize the value in the `StaticStorage` and return a mutable |
| /// reference to it. |
| /// |
| /// # Safety |
| /// The user must ensure that this method is only called once. |
| #[allow(clippy::mut_from_ref)] |
| pub unsafe fn init(&self, val: T) -> &mut T { |
| unsafe { (*self.inner.get()).write(val) } |
| } |
| |
| /// Return the address of the underlying storage as an untyped pointer. |
| /// |
| /// There are circumstances where the address of the static storage needs to |
| /// be known at `const` time. One example of this is adding debug annotation |
| /// to an image. |
| /// |
| /// # Safety |
| /// |
| /// It is up to the caller to ensure that all uses of the returned addresses |
| /// are safe and sound with respect to uses of `init()` |
| pub const unsafe fn address(&self) -> *const () { |
| self.inner.get() as *const () |
| } |
| } |
| |
| // SAFETY: By contract, the user will only call `init()` once therefore only |
| // creating a single reference to the inner data. |
| unsafe impl<T> Sync for StaticStorage<T> {} |
| |
| /// Declare a [`ForeignBox`] in static storage that is runtime initialized. |
| /// |
| /// # Safety |
| /// Caller must ensure that the macro is executed only once. |
| #[macro_export] |
| macro_rules! static_foreign_box { |
| ($ty:ty, $init:expr) => {{ |
| unsafe fn declare_static(val: $ty) -> &'static mut $ty { |
| use $crate::StaticStorage; |
| |
| static STORAGE: StaticStorage<$ty> = StaticStorage::new(); |
| unsafe { STORAGE.init(val) } |
| } |
| let r = declare_static($init); |
| |
| // ForeignBox created outside of function to allow type coercion. |
| $crate::ForeignBox::new(NonNull::from_ref(r)) |
| }}; |
| } |
| |
| /// Declare a [`ForeignRc`] in static storage that is runtime initialized. |
| /// |
| /// # Safety |
| /// Caller must ensure that the macro is executed only once. |
| #[macro_export] |
| macro_rules! static_foreign_rc { |
| ($atomic_usize:ty, $ty:ty, $init:expr) => {{ |
| unsafe fn declare_static(val: $ty) -> $crate::ForeignRc<$atomic_usize, $ty> { |
| use $crate::{ForeignRcState, StaticStorage}; |
| |
| static STORAGE: StaticStorage<ForeignRcState<$atomic_usize, $ty>> = |
| StaticStorage::new(); |
| unsafe { |
| let r = STORAGE.init(ForeignRcState::new(val)); |
| r.create_first_ref() |
| } |
| } |
| |
| declare_static($init) |
| }}; |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use unittest::test; |
| |
| use super::*; |
| |
| #[test] |
| fn consume_returns_the_same_pointer() -> unittest::Result<()> { |
| let mut value = 0xdecafbad_u32; |
| let ptr = &raw mut value; |
| let foreign_box = unsafe { ForeignBox::new(NonNull::new_unchecked(ptr)) }; |
| let consumed_ptr = foreign_box.consume(); |
| |
| unittest::assert_eq!(ptr, consumed_ptr.as_ptr()); |
| Ok(()) |
| } |
| |
| #[test] |
| fn value_can_be_read_through_as_ref() -> unittest::Result<()> { |
| let mut value = 0xdecafbad_u32; |
| let ptr = &raw mut value; |
| let foreign_box = unsafe { ForeignBox::new(NonNull::new_unchecked(ptr)) }; |
| unittest::assert_eq!(value, *(foreign_box.as_ref())); |
| |
| // Prevent foreign_box from being dropped. |
| let _ = foreign_box.consume(); |
| Ok(()) |
| } |
| |
| #[test] |
| fn value_can_be_modified_through_as_mut() -> unittest::Result<()> { |
| let mut value = 0xdecafbad_u32; |
| let ptr = &raw mut value; |
| let mut foreign_box = unsafe { ForeignBox::new(NonNull::new_unchecked(ptr)) }; |
| *(foreign_box.as_mut()) = 0xcafecafe; |
| |
| unittest::assert_eq!(unsafe { ptr.read_volatile() }, 0xcafecafe); |
| |
| // Prevent foreign_box from being dropped. |
| let _ = foreign_box.consume(); |
| Ok(()) |
| } |
| |
| #[test] |
| fn value_can_be_read_through_deref() -> unittest::Result<()> { |
| let mut value = 0xdecafbad_u32; |
| let ptr = &raw mut value; |
| let foreign_box = unsafe { ForeignBox::new(NonNull::new_unchecked(ptr)) }; |
| unittest::assert_eq!(value, *foreign_box); |
| |
| // Prevent foreign_box from being dropped. |
| let _ = foreign_box.consume(); |
| Ok(()) |
| } |
| |
| #[test] |
| fn value_can_be_modified_through_deref_mut() -> unittest::Result<()> { |
| let mut value = 0xdecafbad_u32; |
| let ptr = &raw mut value; |
| let mut foreign_box = unsafe { ForeignBox::new(NonNull::new_unchecked(ptr)) }; |
| *foreign_box = 0xcafecafe; |
| |
| unittest::assert_eq!(unsafe { ptr.read_volatile() }, 0xcafecafe); |
| |
| // Prevent foreign_box from being dropped. |
| let _ = foreign_box.consume(); |
| Ok(()) |
| } |
| |
| trait TestDynTrait { |
| fn number(&self) -> u32; |
| } |
| |
| struct TimesOne { |
| val: u32, |
| } |
| |
| impl TestDynTrait for TimesOne { |
| fn number(&self) -> u32 { |
| self.val |
| } |
| } |
| |
| #[allow(dead_code)] |
| struct TimesTwo { |
| val: u32, |
| } |
| |
| #[allow(dead_code)] |
| impl TestDynTrait for TimesTwo { |
| fn number(&self) -> u32 { |
| self.val * 2 |
| } |
| } |
| |
| fn get_number(val: &ForeignBox<dyn TestDynTrait>) -> u32 { |
| val.as_ref().number() |
| } |
| |
| #[test] |
| fn supports_dynamic_dispatch() -> unittest::Result<()> { |
| let mut times_one_val = TimesOne { val: 10 }; |
| let ptr = &raw mut times_one_val; |
| let times_one_box = unsafe { ForeignBox::<dyn TestDynTrait>::new_from_ptr(ptr) }; |
| |
| let mut times_two_val = TimesTwo { val: 10 }; |
| let ptr = &raw mut times_two_val; |
| let times_two_box = unsafe { ForeignBox::<dyn TestDynTrait>::new_from_ptr(ptr) }; |
| |
| unittest::assert_eq!(get_number(×_one_box), 10); |
| unittest::assert_eq!(get_number(×_two_box), 20); |
| |
| times_one_box.consume(); |
| times_two_box.consume(); |
| Ok(()) |
| } |
| |
| #[test] |
| fn static_foreign_box_can_be_created_and_consumed() -> unittest::Result<()> { |
| let b = unsafe { static_foreign_box!(u32, 42) }; |
| unittest::assert_eq!(*b, 42); |
| let ptr = b.consume(); |
| unittest::assert_eq!(unsafe { ptr.read() }, 42); |
| Ok(()) |
| } |
| |
| #[test] |
| fn static_foreign_rc_ref_count_is_correct() -> unittest::Result<()> { |
| let rc = unsafe { static_foreign_rc!(core::sync::atomic::AtomicUsize, u32, 42) }; |
| unittest::assert_eq!(*rc, 42); |
| unittest::assert_eq!(rc.state.ref_count.load(Ordering::SeqCst), 1); |
| let rc2 = rc.clone(); |
| unittest::assert_eq!(rc.state.ref_count.load(Ordering::SeqCst), 2); |
| drop(rc2); |
| unittest::assert_eq!(rc.state.ref_count.load(Ordering::SeqCst), 1); |
| Ok(()) |
| } |
| } |