use core::ptr::NonNull;
#[cfg(debug_assertions)]
use core::sync::atomic::{AtomicU64, Ordering};
pub struct LiveMarker {
#[cfg(debug_assertions)]
generation: AtomicU64,
}
impl LiveMarker {
pub const DEAD: u64 = 0;
#[inline]
pub fn new() -> Self {
#[cfg(debug_assertions)]
{
Self {
generation: AtomicU64::new(next_gen()),
}
}
#[cfg(not(debug_assertions))]
{
Self {}
}
}
#[inline]
pub fn generation(&self) -> u64 {
#[cfg(debug_assertions)]
{
self.generation.load(Ordering::Relaxed)
}
#[cfg(not(debug_assertions))]
{
1
}
}
#[inline]
pub fn assert_live(&self, expected: u64) {
#[cfg(debug_assertions)]
{
let live = self.generation.load(Ordering::Relaxed);
debug_assert_eq!(
live, expected,
"ParentRef: use-after-parent-drop (marker={live:#x}, expected={expected:#x})",
);
}
#[cfg(not(debug_assertions))]
{
let _ = expected;
}
}
}
impl Default for LiveMarker {
#[inline]
fn default() -> Self {
Self::new()
}
}
#[cfg(debug_assertions)]
impl Drop for LiveMarker {
#[inline]
fn drop(&mut self) {
self.generation.store(Self::DEAD, Ordering::Relaxed);
}
}
#[cfg(debug_assertions)]
#[inline]
fn next_gen() -> u64 {
static GEN: AtomicU64 = AtomicU64::new(1);
GEN.fetch_add(1, Ordering::Relaxed)
}
pub trait Anchored {
fn live_marker(&self) -> &LiveMarker;
}
#[cfg_attr(not(debug_assertions), repr(transparent))]
pub struct ParentRef<T: ?Sized> {
ptr: NonNull<T>,
#[cfg(debug_assertions)]
generation: u64,
#[cfg(debug_assertions)]
marker: Option<NonNull<AtomicU64>>,
}
impl<T: ?Sized> ParentRef<T> {
#[inline]
pub fn new(parent: &T) -> Self {
Self {
ptr: NonNull::from(parent),
#[cfg(debug_assertions)]
generation: 0,
#[cfg(debug_assertions)]
marker: None,
}
}
#[inline]
pub fn anchored(parent: &T) -> Self
where
T: Anchored,
{
#[cfg(debug_assertions)]
{
let marker = parent.live_marker();
Self {
ptr: NonNull::from(parent),
generation: marker.generation(),
marker: Some(NonNull::from(&marker.generation)),
}
}
#[cfg(not(debug_assertions))]
{
Self {
ptr: NonNull::from(parent),
}
}
}
#[inline]
pub const unsafe fn from_raw(p: *const T) -> Self {
Self {
ptr: unsafe { NonNull::new_unchecked(p.cast_mut()) },
#[cfg(debug_assertions)]
generation: 0,
#[cfg(debug_assertions)]
marker: None,
}
}
#[inline]
pub const unsafe fn from_raw_mut(p: *mut T) -> Self {
Self {
ptr: unsafe { NonNull::new_unchecked(p) },
#[cfg(debug_assertions)]
generation: 0,
#[cfg(debug_assertions)]
marker: None,
}
}
#[inline]
pub unsafe fn from_nullable_mut(p: *mut T) -> Option<Self> {
NonNull::new(p).map(|nn| Self {
ptr: nn,
#[cfg(debug_assertions)]
generation: 0,
#[cfg(debug_assertions)]
marker: None,
})
}
#[cfg(debug_assertions)]
#[inline]
fn debug_assert_live(&self) {
if let Some(m) = self.marker {
let live = unsafe { m.as_ref() }.load(Ordering::Relaxed);
debug_assert_eq!(
live,
self.generation,
"ParentRef<{}>: use-after-parent-drop (live={live:#x}, snap={:#x})",
core::any::type_name::<T>(),
self.generation,
);
}
}
#[inline]
pub fn get(&self) -> &T {
#[cfg(debug_assertions)]
self.debug_assert_live();
unsafe { self.ptr.as_ref() }
}
#[inline]
pub fn as_ptr(self) -> *const T {
self.ptr.as_ptr()
}
#[inline]
pub fn as_mut_ptr(self) -> *mut T {
self.ptr.as_ptr()
}
#[inline]
pub unsafe fn assume_mut<'a>(self) -> &'a mut T {
#[cfg(debug_assertions)]
self.debug_assert_live();
unsafe { &mut *self.ptr.as_ptr() }
}
}
impl<T: ?Sized> Copy for ParentRef<T> {}
impl<T: ?Sized> Clone for ParentRef<T> {
#[inline]
fn clone(&self) -> Self {
*self
}
}
impl<T: ?Sized> core::ops::Deref for ParentRef<T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
self.get()
}
}
impl<T: ?Sized> From<NonNull<T>> for ParentRef<T> {
#[inline]
fn from(p: NonNull<T>) -> Self {
Self {
ptr: p,
#[cfg(debug_assertions)]
generation: 0,
#[cfg(debug_assertions)]
marker: None,
}
}
}
impl<T: ?Sized> core::fmt::Debug for ParentRef<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
#[cfg(debug_assertions)]
{
write!(
f,
"ParentRef({:p}, generation={:#x})",
self.ptr, self.generation
)
}
#[cfg(not(debug_assertions))]
{
write!(f, "ParentRef({:p})", self.ptr)
}
}
}
impl<T: ?Sized> PartialEq for ParentRef<T> {
#[inline]
fn eq(&self, other: &Self) -> bool {
core::ptr::addr_eq(self.ptr.as_ptr(), other.ptr.as_ptr())
}
}
impl<T: ?Sized> Eq for ParentRef<T> {}
unsafe impl<T: ?Sized + Sync> Send for ParentRef<T> {}
unsafe impl<T: ?Sized + Sync> Sync for ParentRef<T> {}
#[cfg(all(test, not(debug_assertions)))]
const _: () = {
assert!(core::mem::size_of::<Option<ParentRef<u8>>>() == core::mem::size_of::<*mut u8>());
};