#![doc = include_str!("../README.md")]
#![warn(missing_docs)]
use parking_lot::Mutex;
use std::{
fmt::Debug,
marker::PhantomData,
ops::{Deref, DerefMut},
sync::Arc,
};
pub use error::*;
pub use references::*;
pub use sync::SyncStorage;
pub use unsync::UnsyncStorage;
mod error;
mod references;
mod sync;
mod unsync;
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct GenerationalBoxId {
data_ptr: *const (),
#[cfg(any(debug_assertions, feature = "check_generation"))]
generation: u32,
}
unsafe impl Send for GenerationalBoxId {}
unsafe impl Sync for GenerationalBoxId {}
impl Debug for GenerationalBoxId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
#[cfg(any(debug_assertions, feature = "check_generation"))]
f.write_fmt(format_args!("{:?}@{:?}", self.data_ptr, self.generation))?;
#[cfg(not(any(debug_assertions, feature = "check_generation")))]
f.write_fmt(format_args!("{:?}", self.data_ptr))?;
Ok(())
}
}
pub struct GenerationalBox<T, S: 'static = UnsyncStorage> {
raw: MemoryLocation<S>,
#[cfg(any(debug_assertions, feature = "check_generation"))]
generation: u32,
#[cfg(any(debug_assertions, feature = "debug_ownership"))]
created_at: &'static std::panic::Location<'static>,
_marker: PhantomData<T>,
}
impl<T, S: AnyStorage> Debug for GenerationalBox<T, S> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
#[cfg(any(debug_assertions, feature = "check_generation"))]
f.write_fmt(format_args!(
"{:?}@{:?}",
self.raw.0.data.data_ptr(),
self.generation
))?;
#[cfg(not(any(debug_assertions, feature = "check_generation")))]
f.write_fmt(format_args!("{:?}", self.raw.0.data.data_ptr()))?;
Ok(())
}
}
impl<T, S: Storage<T>> GenerationalBox<T, S> {
#[track_caller]
pub fn leak(value: T) -> Self {
let mut location = S::claim();
location.replace_with_caller(
value,
#[cfg(any(debug_assertions, feature = "debug_ownership"))]
std::panic::Location::caller(),
)
}
#[inline(always)]
pub(crate) fn validate(&self) -> bool {
#[cfg(any(debug_assertions, feature = "check_generation"))]
{
self.raw
.0
.generation
.load(std::sync::atomic::Ordering::Relaxed)
== self.generation
}
#[cfg(not(any(debug_assertions, feature = "check_generation")))]
{
true
}
}
pub fn raw_ptr(&self) -> *const () {
self.raw.0.data.data_ptr()
}
pub fn id(&self) -> GenerationalBoxId {
GenerationalBoxId {
data_ptr: self.raw.0.data.data_ptr(),
#[cfg(any(debug_assertions, feature = "check_generation"))]
generation: self.generation,
}
}
#[track_caller]
pub fn try_read(&self) -> Result<S::Ref<'static, T>, BorrowError> {
if !self.validate() {
return Err(BorrowError::Dropped(ValueDroppedError {
#[cfg(any(debug_assertions, feature = "debug_borrows"))]
created_at: self.created_at,
}));
}
let result = self.raw.0.data.try_read(
#[cfg(any(debug_assertions, feature = "debug_ownership"))]
GenerationalRefBorrowInfo {
borrowed_at: std::panic::Location::caller(),
borrowed_from: &self.raw.0.borrow,
created_at: self.created_at,
},
);
if result.is_ok() {
#[cfg(any(debug_assertions, feature = "debug_ownership"))]
self.raw
.0
.borrow
.borrowed_at
.write()
.push(std::panic::Location::caller());
}
result
}
#[track_caller]
pub fn read(&self) -> S::Ref<'static, T> {
self.try_read().unwrap()
}
#[track_caller]
pub fn try_write(&self) -> Result<S::Mut<'static, T>, BorrowMutError> {
if !self.validate() {
return Err(BorrowMutError::Dropped(ValueDroppedError {
#[cfg(any(debug_assertions, feature = "debug_borrows"))]
created_at: self.created_at,
}));
}
let result = self.raw.0.data.try_write(
#[cfg(any(debug_assertions, feature = "debug_ownership"))]
GenerationalRefMutBorrowInfo {
borrowed_from: &self.raw.0.borrow,
created_at: self.created_at,
},
);
if result.is_ok() {
#[cfg(any(debug_assertions, feature = "debug_ownership"))]
{
*self.raw.0.borrow.borrowed_mut_at.write() = Some(std::panic::Location::caller());
}
}
result
}
#[track_caller]
pub fn write(&self) -> S::Mut<'static, T> {
self.try_write().unwrap()
}
pub fn set(&self, value: T) {
self.validate().then(|| {
self.raw.0.data.set(value);
});
}
pub fn ptr_eq(&self, other: &Self) -> bool {
#[cfg(any(debug_assertions, feature = "check_generation"))]
{
self.raw.0.data.data_ptr() == other.raw.0.data.data_ptr()
&& self.generation == other.generation
}
#[cfg(not(any(debug_assertions, feature = "check_generation")))]
{
self.raw.0.data.data_ptr() == other.raw.0.data.data_ptr()
}
}
pub fn recycle(&self) {
if self.validate() {
<S as AnyStorage>::recycle(&self.raw);
}
}
pub fn manually_drop(&self) -> Option<T> {
if self.validate() {
let value = Storage::take(&self.raw.0.data)?;
<S as AnyStorage>::recycle(&self.raw);
Some(value)
} else {
None
}
}
}
impl<T, S> Copy for GenerationalBox<T, S> {}
impl<T, S> Clone for GenerationalBox<T, S> {
fn clone(&self) -> Self {
*self
}
}
pub trait Storage<Data = ()>: AnyStorage + 'static {
fn try_read(
&'static self,
#[cfg(any(debug_assertions, feature = "debug_ownership"))] at: GenerationalRefBorrowInfo,
) -> Result<Self::Ref<'static, Data>, BorrowError>;
fn try_write(
&'static self,
#[cfg(any(debug_assertions, feature = "debug_ownership"))] at: GenerationalRefMutBorrowInfo,
) -> Result<Self::Mut<'static, Data>, BorrowMutError>;
fn set(&'static self, value: Data);
fn take(&'static self) -> Option<Data>;
}
pub trait AnyStorage: Default {
type Ref<'a, T: ?Sized + 'static>: Deref<Target = T>;
type Mut<'a, T: ?Sized + 'static>: DerefMut<Target = T>;
fn downcast_lifetime_ref<'a: 'b, 'b, T: ?Sized + 'static>(
ref_: Self::Ref<'a, T>,
) -> Self::Ref<'b, T>;
fn downcast_lifetime_mut<'a: 'b, 'b, T: ?Sized + 'static>(
mut_: Self::Mut<'a, T>,
) -> Self::Mut<'b, T>;
fn try_map_mut<T: ?Sized + 'static, U: ?Sized + 'static>(
mut_ref: Self::Mut<'_, T>,
f: impl FnOnce(&mut T) -> Option<&mut U>,
) -> Option<Self::Mut<'_, U>>;
fn map_mut<T: ?Sized + 'static, U: ?Sized + 'static>(
mut_ref: Self::Mut<'_, T>,
f: impl FnOnce(&mut T) -> &mut U,
) -> Self::Mut<'_, U> {
Self::try_map_mut(mut_ref, |v| Some(f(v))).unwrap()
}
fn try_map<T: ?Sized, U: ?Sized + 'static>(
ref_: Self::Ref<'_, T>,
f: impl FnOnce(&T) -> Option<&U>,
) -> Option<Self::Ref<'_, U>>;
fn map<T: ?Sized, U: ?Sized + 'static>(
ref_: Self::Ref<'_, T>,
f: impl FnOnce(&T) -> &U,
) -> Self::Ref<'_, U> {
Self::try_map(ref_, |v| Some(f(v))).unwrap()
}
fn data_ptr(&self) -> *const ();
fn manually_drop(&self) -> bool;
fn recycle(location: &MemoryLocation<Self>);
fn claim() -> MemoryLocation<Self>;
fn owner() -> Owner<Self> {
Owner(Arc::new(Mutex::new(OwnerInner {
owned: Default::default(),
})))
}
}
pub struct MemoryLocation<S: 'static = UnsyncStorage>(&'static MemoryLocationInner<S>);
impl<S: 'static> Clone for MemoryLocation<S> {
fn clone(&self) -> Self {
*self
}
}
impl<S: 'static> Copy for MemoryLocation<S> {}
#[cfg(any(debug_assertions, feature = "debug_borrows"))]
#[derive(Debug, Default)]
struct MemoryLocationBorrowInfo {
pub(crate) borrowed_at: parking_lot::RwLock<Vec<&'static std::panic::Location<'static>>>,
pub(crate) borrowed_mut_at: parking_lot::RwLock<Option<&'static std::panic::Location<'static>>>,
}
#[cfg(any(debug_assertions, feature = "debug_ownership"))]
impl MemoryLocationBorrowInfo {
fn borrow_mut_error(&self) -> BorrowMutError {
if let Some(borrowed_mut_at) = self.borrowed_mut_at.read().as_ref() {
BorrowMutError::AlreadyBorrowedMut(crate::error::AlreadyBorrowedMutError {
#[cfg(any(debug_assertions, feature = "debug_borrows"))]
borrowed_mut_at,
})
} else {
BorrowMutError::AlreadyBorrowed(crate::error::AlreadyBorrowedError {
#[cfg(any(debug_assertions, feature = "debug_borrows"))]
borrowed_at: self.borrowed_at.read().clone(),
})
}
}
fn borrow_error(&self) -> BorrowError {
BorrowError::AlreadyBorrowedMut(crate::error::AlreadyBorrowedMutError {
#[cfg(any(debug_assertions, feature = "debug_ownership"))]
borrowed_mut_at: self.borrowed_mut_at.read().unwrap(),
})
}
}
struct MemoryLocationInner<S = UnsyncStorage> {
data: S,
#[cfg(any(debug_assertions, feature = "check_generation"))]
generation: std::sync::atomic::AtomicU32,
#[cfg(any(debug_assertions, feature = "debug_borrows"))]
borrow: MemoryLocationBorrowInfo,
}
impl<S> MemoryLocation<S> {
#[allow(unused)]
fn drop(&self)
where
S: AnyStorage,
{
self.0.data.manually_drop();
#[cfg(any(debug_assertions, feature = "check_generation"))]
{
let new_generation = self.0.generation.load(std::sync::atomic::Ordering::Relaxed) + 1;
self.0
.generation
.store(new_generation, std::sync::atomic::Ordering::Relaxed);
}
}
fn replace_with_caller<T>(
&mut self,
value: T,
#[cfg(any(debug_assertions, feature = "debug_ownership"))]
caller: &'static std::panic::Location<'static>,
) -> GenerationalBox<T, S>
where
S: Storage<T>,
{
self.0.data.set(value);
GenerationalBox {
raw: *self,
#[cfg(any(debug_assertions, feature = "check_generation"))]
generation: self.0.generation.load(std::sync::atomic::Ordering::Relaxed),
#[cfg(any(debug_assertions, feature = "debug_ownership"))]
created_at: caller,
_marker: PhantomData,
}
}
}
struct LocationKey<S: 'static> {
#[cfg(any(debug_assertions, feature = "check_generation"))]
generation: u32,
location: MemoryLocation<S>,
}
impl<S: AnyStorage> LocationKey<S> {
fn exists(&self) -> bool {
#[cfg(any(debug_assertions, feature = "check_generation"))]
return self.generation
== self
.location
.0
.generation
.load(std::sync::atomic::Ordering::Relaxed);
#[cfg(not(any(debug_assertions, feature = "check_generation")))]
return true;
}
fn drop(self) {
if self.exists() {
S::recycle(&self.location);
}
}
}
impl<T, S: AnyStorage> From<GenerationalBox<T, S>> for LocationKey<S> {
fn from(value: GenerationalBox<T, S>) -> Self {
Self {
#[cfg(any(debug_assertions, feature = "check_generation"))]
generation: value.generation,
location: value.raw,
}
}
}
struct OwnerInner<S: AnyStorage + 'static> {
owned: Vec<LocationKey<S>>,
}
impl<S: AnyStorage> Drop for OwnerInner<S> {
fn drop(&mut self) {
for key in self.owned.drain(..) {
key.drop();
}
}
}
pub struct Owner<S: AnyStorage + 'static = UnsyncStorage>(Arc<Mutex<OwnerInner<S>>>);
impl<S: AnyStorage> Default for Owner<S> {
fn default() -> Self {
S::owner()
}
}
impl<S: AnyStorage> Clone for Owner<S> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<S: AnyStorage> Owner<S> {
#[track_caller]
pub fn insert<T: 'static>(&self, value: T) -> GenerationalBox<T, S>
where
S: Storage<T>,
{
self.insert_with_caller(
value,
#[cfg(any(debug_assertions, feature = "debug_ownership"))]
std::panic::Location::caller(),
)
}
pub fn insert_with_caller<T: 'static>(
&self,
value: T,
#[cfg(any(debug_assertions, feature = "debug_ownership"))]
caller: &'static std::panic::Location<'static>,
) -> GenerationalBox<T, S>
where
S: Storage<T>,
{
let mut location = S::claim();
let key = location.replace_with_caller(
value,
#[cfg(any(debug_assertions, feature = "debug_borrows"))]
caller,
);
self.0.lock().owned.push(key.into());
key
}
pub fn invalid<T: 'static>(&self) -> GenerationalBox<T, S> {
let location = S::claim();
let generational_box = GenerationalBox {
raw: location,
#[cfg(any(debug_assertions, feature = "check_generation"))]
generation: location
.0
.generation
.load(std::sync::atomic::Ordering::Relaxed),
#[cfg(any(debug_assertions, feature = "debug_ownership"))]
created_at: std::panic::Location::caller(),
_marker: PhantomData,
};
self.0.lock().owned.push(generational_box.into());
generational_box
}
}