simplify generational box by compressing debug info

This commit is contained in:
Jonathan Kelley 2024-01-22 21:17:02 -08:00
parent 27f8377ea5
commit 079fec3be6
No known key found for this signature in database
GPG key ID: 1FBB50F7EB0A08BE
7 changed files with 162 additions and 163 deletions

View file

@ -20,138 +20,6 @@ mod references;
mod sync;
mod unsync;
/// # Example
///
/// ```compile_fail
/// let data = String::from("hello world");
/// let owner = UnsyncStorage::owner();
/// let key = owner.insert(&data);
/// drop(data);
/// assert_eq!(*key.read(), "hello world");
/// ```
#[allow(unused)]
fn compile_fail() {}
#[test]
fn reused() {
let first_ptr;
{
let owner = UnsyncStorage::owner();
first_ptr = owner.insert(1).raw.0.data.data_ptr();
drop(owner);
}
{
let owner = UnsyncStorage::owner();
let second_ptr = owner.insert(1234).raw.0.data.data_ptr();
assert_eq!(first_ptr, second_ptr);
drop(owner);
}
}
#[test]
fn leaking_is_ok() {
let data = String::from("hello world");
let key;
{
// create an owner
let owner = UnsyncStorage::owner();
// insert data into the store
key = owner.insert(data);
// don't drop the owner
std::mem::forget(owner);
}
assert_eq!(
key.try_read().as_deref().unwrap(),
&"hello world".to_string()
);
}
#[test]
fn drops() {
let data = String::from("hello world");
let key;
{
// create an owner
let owner = UnsyncStorage::owner();
// insert data into the store
key = owner.insert(data);
// drop the owner
}
assert!(key.try_read().is_err());
}
#[test]
fn works() {
let owner = UnsyncStorage::owner();
let key = owner.insert(1);
assert_eq!(*key.read(), 1);
}
#[test]
fn insert_while_reading() {
let owner = UnsyncStorage::owner();
let key;
{
let data: String = "hello world".to_string();
key = owner.insert(data);
}
let value = key.read();
owner.insert(&1);
assert_eq!(*value, "hello world");
}
#[test]
#[should_panic]
fn panics() {
let owner = UnsyncStorage::owner();
let key = owner.insert(1);
drop(owner);
assert_eq!(*key.read(), 1);
}
#[test]
fn fuzz() {
fn maybe_owner_scope(
valid_keys: &mut Vec<GenerationalBox<String>>,
invalid_keys: &mut Vec<GenerationalBox<String>>,
path: &mut Vec<u8>,
) {
let branch_cutoff = 5;
let children = if path.len() < branch_cutoff {
rand::random::<u8>() % 4
} else {
rand::random::<u8>() % 2
};
for i in 0..children {
let owner = UnsyncStorage::owner();
let key = owner.insert(format!("hello world {path:?}"));
valid_keys.push(key);
path.push(i);
// read all keys
println!("{:?}", path);
for key in valid_keys.iter() {
let value = key.read();
println!("{:?}", &*value);
assert!(value.starts_with("hello world"));
}
#[cfg(any(debug_assertions, feature = "check_generation"))]
for key in invalid_keys.iter() {
assert!(!key.validate());
}
maybe_owner_scope(valid_keys, invalid_keys, path);
invalid_keys.push(valid_keys.pop().unwrap());
path.pop();
}
}
for _ in 0..10 {
maybe_owner_scope(&mut Vec::new(), &mut Vec::new(), &mut Vec::new());
}
}
/// The type erased id of a generational box.
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct GenerationalBoxId {
@ -200,7 +68,7 @@ impl<T: 'static, S: AnyStorage> Debug for GenerationalBox<T, S> {
impl<T: 'static, S: Storage<T>> GenerationalBox<T, S> {
#[inline(always)]
fn validate(&self) -> bool {
pub fn validate(&self) -> bool {
#[cfg(any(debug_assertions, feature = "check_generation"))]
{
self.raw
@ -215,6 +83,11 @@ impl<T: 'static, S: Storage<T>> GenerationalBox<T, S> {
}
}
/// Get the raw pointer to the value.
pub fn raw_ptr(&self) -> *const () {
self.raw.0.data.data_ptr()
}
/// Get the id of the generational box.
pub fn id(&self) -> GenerationalBoxId {
GenerationalBoxId {
@ -234,12 +107,11 @@ impl<T: 'static, S: Storage<T>> GenerationalBox<T, S> {
}));
}
let result = self.raw.0.data.try_read(
#[cfg(any(debug_assertions, feature = "debug_ownership"))]
self.created_at,
#[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,
},
);
@ -272,11 +144,10 @@ impl<T: 'static, S: Storage<T>> GenerationalBox<T, S> {
}));
}
let result = self.raw.0.data.try_write(
#[cfg(any(debug_assertions, feature = "debug_ownership"))]
self.created_at,
#[cfg(any(debug_assertions, feature = "debug_ownership"))]
GenerationalRefMutBorrowInfo {
borrowed_from: &self.raw.0.borrow,
created_at: self.created_at,
},
);
@ -360,16 +231,12 @@ pub trait Storage<Data = ()>: AnyStorage + 'static {
/// Try to read the value. Returns None if the value is no longer valid.
fn try_read(
&'static self,
#[cfg(any(debug_assertions, feature = "debug_ownership"))]
created_at: &'static std::panic::Location<'static>,
#[cfg(any(debug_assertions, feature = "debug_ownership"))] at: GenerationalRefBorrowInfo,
) -> Result<Self::Ref<Data>, BorrowError>;
/// Try to write the value. Returns None if the value is no longer valid.
fn try_write(
&'static self,
#[cfg(any(debug_assertions, feature = "debug_ownership"))]
created_at: &'static std::panic::Location<'static>,
#[cfg(any(debug_assertions, feature = "debug_ownership"))] at: GenerationalRefMutBorrowInfo,
) -> Result<Self::Mut<Data>, BorrowMutError>;
@ -444,8 +311,10 @@ impl MemoryLocationBorrowInfo {
struct MemoryLocationInner<S = UnsyncStorage> {
data: S,
#[cfg(any(debug_assertions, feature = "check_generation"))]
generation: AtomicU32,
#[cfg(any(debug_assertions, feature = "debug_borrows"))]
borrow: MemoryLocationBorrowInfo,
}

View file

@ -48,6 +48,7 @@ impl<T: ?Sized + 'static, R: Deref<Target = T>> Deref for GenerationalRef<R> {
pub struct GenerationalRefBorrowInfo {
pub(crate) borrowed_at: &'static std::panic::Location<'static>,
pub(crate) borrowed_from: &'static crate::MemoryLocationBorrowInfo,
pub(crate) created_at: &'static std::panic::Location<'static>,
}
#[cfg(any(debug_assertions, feature = "debug_borrows"))]
@ -100,6 +101,7 @@ impl<T: ?Sized + 'static, W: DerefMut<Target = T>> DerefMut for GenerationalRefM
pub struct GenerationalRefMutBorrowInfo {
/// The location where the borrow occurred.
pub(crate) borrowed_from: &'static crate::MemoryLocationBorrowInfo,
pub(crate) created_at: &'static std::panic::Location<'static>,
}
#[cfg(any(debug_assertions, feature = "debug_borrows"))]

View file

@ -56,8 +56,6 @@ impl<T: Sync + Send + 'static> Storage<T> for SyncStorage {
fn try_read(
&'static self,
#[cfg(any(debug_assertions, feature = "debug_ownership"))]
created_at: &'static std::panic::Location<'static>,
#[cfg(any(debug_assertions, feature = "debug_ownership"))]
at: crate::GenerationalRefBorrowInfo,
) -> Result<Self::Ref<T>, error::BorrowError> {
let read = self.0.try_read();
@ -74,7 +72,7 @@ impl<T: Sync + Send + 'static> Storage<T> for SyncStorage {
.map_err(|_| {
error::BorrowError::Dropped(ValueDroppedError {
#[cfg(any(debug_assertions, feature = "debug_ownership"))]
created_at,
created_at: at.created_at,
})
})
.map(|guard| {
@ -89,8 +87,6 @@ impl<T: Sync + Send + 'static> Storage<T> for SyncStorage {
fn try_write(
&'static self,
#[cfg(any(debug_assertions, feature = "debug_ownership"))]
created_at: &'static std::panic::Location<'static>,
#[cfg(any(debug_assertions, feature = "debug_ownership"))]
at: crate::GenerationalRefMutBorrowInfo,
) -> Result<Self::Mut<T>, error::BorrowMutError> {
let write = self.0.try_write();
@ -107,7 +103,7 @@ impl<T: Sync + Send + 'static> Storage<T> for SyncStorage {
.map_err(|_| {
error::BorrowMutError::Dropped(ValueDroppedError {
#[cfg(any(debug_assertions, feature = "debug_ownership"))]
created_at,
created_at: at.created_at,
})
})
.map(|guard| {
@ -141,6 +137,7 @@ impl<T: Sync + Send + 'static> Storage<T> for SyncStorage {
borrow: GenerationalRefBorrowInfo {
borrowed_at: borrow.borrowed_at,
borrowed_from: borrow.borrowed_from,
created_at: borrow.created_at,
},
})
}
@ -162,6 +159,7 @@ impl<T: Sync + Send + 'static> Storage<T> for SyncStorage {
#[cfg(any(debug_assertions, feature = "debug_borrows"))]
borrow: GenerationalRefMutBorrowInfo {
borrowed_from: borrow.borrowed_from,
created_at: borrow.created_at,
},
})
}

View file

@ -6,22 +6,16 @@ use crate::{
use std::cell::{Ref, RefCell, RefMut};
/// A unsync storage. This is the default storage type.
#[derive(Default)]
pub struct UnsyncStorage(RefCell<Option<Box<dyn std::any::Any>>>);
impl Default for UnsyncStorage {
fn default() -> Self {
Self(RefCell::new(None))
}
}
impl<T: 'static> Storage<T> for UnsyncStorage {
type Ref<R: ?Sized + 'static> = GenerationalRef<Ref<'static, R>>;
type Mut<W: ?Sized + 'static> = GenerationalRefMut<RefMut<'static, W>>;
fn try_read(
&'static self,
#[cfg(any(debug_assertions, feature = "debug_ownership"))]
created_at: &'static std::panic::Location<'static>,
#[cfg(any(debug_assertions, feature = "debug_ownership"))]
at: crate::GenerationalRefBorrowInfo,
) -> Result<Self::Ref<T>, error::BorrowError> {
@ -39,7 +33,7 @@ impl<T: 'static> Storage<T> for UnsyncStorage {
.map_err(|_| {
error::BorrowError::Dropped(error::ValueDroppedError {
#[cfg(any(debug_assertions, feature = "debug_ownership"))]
created_at,
created_at: at.created_at,
})
})
.map(|guard| {
@ -54,8 +48,6 @@ impl<T: 'static> Storage<T> for UnsyncStorage {
fn try_write(
&'static self,
#[cfg(any(debug_assertions, feature = "debug_ownership"))]
created_at: &'static std::panic::Location<'static>,
#[cfg(any(debug_assertions, feature = "debug_ownership"))]
at: crate::GenerationalRefMutBorrowInfo,
) -> Result<Self::Mut<T>, error::BorrowMutError> {
let borrow = self.0.try_borrow_mut();
@ -71,7 +63,7 @@ impl<T: 'static> Storage<T> for UnsyncStorage {
.map_err(|_| {
error::BorrowMutError::Dropped(error::ValueDroppedError {
#[cfg(any(debug_assertions, feature = "debug_ownership"))]
created_at,
created_at: at.created_at,
})
})
.map(|guard| {
@ -121,6 +113,7 @@ impl<T: 'static> Storage<T> for UnsyncStorage {
#[cfg(any(debug_assertions, feature = "debug_borrows"))]
borrow: GenerationalRefMutBorrowInfo {
borrowed_from: borrow.borrowed_from,
created_at: borrow.created_at,
},
})
}

View file

@ -0,0 +1,133 @@
use generational_box::{AnyStorage, GenerationalBox, UnsyncStorage};
/// # Example
///
/// ```compile_fail
/// let data = String::from("hello world");
/// let owner = UnsyncStorage::owner();
/// let key = owner.insert(&data);
/// drop(data);
/// assert_eq!(*key.read(), "hello world");
/// ```
#[allow(unused)]
fn compile_fail() {}
#[test]
fn reused() {
let first_ptr;
{
let owner = UnsyncStorage::owner();
first_ptr = owner.insert(1).raw_ptr();
drop(owner);
}
{
let owner = UnsyncStorage::owner();
let second_ptr = owner.insert(1234).raw_ptr();
assert_eq!(first_ptr, second_ptr);
drop(owner);
}
}
#[test]
fn leaking_is_ok() {
let data = String::from("hello world");
let key;
{
// create an owner
let owner = UnsyncStorage::owner();
// insert data into the store
key = owner.insert(data);
// don't drop the owner
std::mem::forget(owner);
}
assert_eq!(
key.try_read().as_deref().unwrap(),
&"hello world".to_string()
);
}
#[test]
fn drops() {
let data = String::from("hello world");
let key;
{
// create an owner
let owner = UnsyncStorage::owner();
// insert data into the store
key = owner.insert(data);
// drop the owner
}
assert!(key.try_read().is_err());
}
#[test]
fn works() {
let owner = UnsyncStorage::owner();
let key = owner.insert(1);
assert_eq!(*key.read(), 1);
}
#[test]
fn insert_while_reading() {
let owner = UnsyncStorage::owner();
let key;
{
let data: String = "hello world".to_string();
key = owner.insert(data);
}
let value = key.read();
owner.insert(&1);
assert_eq!(*value, "hello world");
}
#[test]
#[should_panic]
fn panics() {
let owner = UnsyncStorage::owner();
let key = owner.insert(1);
drop(owner);
assert_eq!(*key.read(), 1);
}
#[test]
fn fuzz() {
fn maybe_owner_scope(
valid_keys: &mut Vec<GenerationalBox<String>>,
invalid_keys: &mut Vec<GenerationalBox<String>>,
path: &mut Vec<u8>,
) {
let branch_cutoff = 5;
let children = if path.len() < branch_cutoff {
rand::random::<u8>() % 4
} else {
rand::random::<u8>() % 2
};
for i in 0..children {
let owner = UnsyncStorage::owner();
let key = owner.insert(format!("hello world {path:?}"));
valid_keys.push(key);
path.push(i);
// read all keys
println!("{:?}", path);
for key in valid_keys.iter() {
let value = key.read();
println!("{:?}", &*value);
assert!(value.starts_with("hello world"));
}
#[cfg(any(debug_assertions, feature = "check_generation"))]
for key in invalid_keys.iter() {
assert!(!key.validate());
}
maybe_owner_scope(valid_keys, invalid_keys, path);
invalid_keys.push(valid_keys.pop().unwrap());
path.pop();
}
}
for _ in 0..10 {
maybe_owner_scope(&mut Vec::new(), &mut Vec::new(), &mut Vec::new());
}
}

View file

@ -22,8 +22,8 @@ pub use dependency::*;
mod map;
pub use map::*;
mod comparer;
pub use comparer::*;
// mod comparer;
// pub use comparer::*;
mod global;
pub use global::*;

View file

@ -14,10 +14,14 @@ pub struct MappedSignal<U: 'static + ?Sized> {
impl MappedSignal<()> {
/// Create a new mapped signal.
pub fn new<T, S: Storage<SignalData<T>>, U: ?Sized>(
pub fn new<T, S, U>(
signal: Signal<T, S>,
mapping: impl Fn(&T) -> &U + 'static,
) -> MappedSignal<S::Ref<U>> {
) -> MappedSignal<S::Ref<U>>
where
S: Storage<SignalData<T>>,
U: ?Sized,
{
MappedSignal {
origin_scope: signal.origin_scope(),
mapping: CopyValue::new(Box::new(move || S::map(signal.read(), &mapping))),