From 9e388c3c51931d42787283dece0ce6532080d70c Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Fri, 15 Dec 2023 08:50:54 -0600 Subject: [PATCH] refactor generational box into multiple files --- packages/generational-box/src/error.rs | 104 ++++ packages/generational-box/src/lib.rs | 637 ++++---------------- packages/generational-box/src/references.rs | 169 ++++++ packages/generational-box/src/sync.rs | 135 +++++ packages/generational-box/src/unsync.rs | 137 +++++ packages/signals/src/signal.rs | 3 +- 6 files changed, 651 insertions(+), 534 deletions(-) create mode 100644 packages/generational-box/src/error.rs create mode 100644 packages/generational-box/src/references.rs create mode 100644 packages/generational-box/src/sync.rs create mode 100644 packages/generational-box/src/unsync.rs diff --git a/packages/generational-box/src/error.rs b/packages/generational-box/src/error.rs new file mode 100644 index 000000000..9aef73901 --- /dev/null +++ b/packages/generational-box/src/error.rs @@ -0,0 +1,104 @@ +use std::error::Error; +use std::fmt::Debug; +use std::fmt::Display; + +#[derive(Debug, Clone)] +/// An error that can occur when trying to borrow a value. +pub enum BorrowError { + /// The value was dropped. + Dropped(ValueDroppedError), + /// The value was already borrowed mutably. + AlreadyBorrowedMut(AlreadyBorrowedMutError), +} + +impl Display for BorrowError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + BorrowError::Dropped(error) => Display::fmt(error, f), + BorrowError::AlreadyBorrowedMut(error) => Display::fmt(error, f), + } + } +} + +impl Error for BorrowError {} + +#[derive(Debug, Clone)] +/// An error that can occur when trying to borrow a value mutably. +pub enum BorrowMutError { + /// The value was dropped. + Dropped(ValueDroppedError), + /// The value was already borrowed. + AlreadyBorrowed(AlreadyBorrowedError), + /// The value was already borrowed mutably. + AlreadyBorrowedMut(AlreadyBorrowedMutError), +} + +impl Display for BorrowMutError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + BorrowMutError::Dropped(error) => Display::fmt(error, f), + BorrowMutError::AlreadyBorrowedMut(error) => Display::fmt(error, f), + BorrowMutError::AlreadyBorrowed(error) => Display::fmt(error, f), + } + } +} + +impl Error for BorrowMutError {} + +/// An error that can occur when trying to use a value that has been dropped. +#[derive(Debug, Copy, Clone)] +pub struct ValueDroppedError { + #[cfg(any(debug_assertions, feature = "debug_ownership"))] + pub(crate) created_at: &'static std::panic::Location<'static>, +} + +impl Display for ValueDroppedError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("Failed to borrow because the value was dropped.")?; + #[cfg(any(debug_assertions, feature = "debug_ownership"))] + f.write_fmt(format_args!("created_at: {}", self.created_at))?; + Ok(()) + } +} + +impl std::error::Error for ValueDroppedError {} + +/// An error that can occur when trying to borrow a value that has already been borrowed mutably. +#[derive(Debug, Copy, Clone)] +pub struct AlreadyBorrowedMutError { + #[cfg(any(debug_assertions, feature = "debug_borrows"))] + pub(crate) borrowed_mut_at: &'static std::panic::Location<'static>, +} + +impl Display for AlreadyBorrowedMutError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("Failed to borrow because the value was already borrowed mutably.")?; + #[cfg(any(debug_assertions, feature = "debug_borrows"))] + f.write_fmt(format_args!("borrowed_mut_at: {}", self.borrowed_mut_at))?; + Ok(()) + } +} + +impl std::error::Error for AlreadyBorrowedMutError {} + +/// An error that can occur when trying to borrow a value mutably that has already been borrowed immutably. +#[derive(Debug, Clone)] +pub struct AlreadyBorrowedError { + #[cfg(any(debug_assertions, feature = "debug_borrows"))] + pub(crate) borrowed_at: Vec<&'static std::panic::Location<'static>>, +} + +impl Display for AlreadyBorrowedError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("Failed to borrow mutably because the value was already borrowed immutably.")?; + #[cfg(any(debug_assertions, feature = "debug_borrows"))] + f.write_str("borrowed_at:")?; + #[cfg(any(debug_assertions, feature = "debug_borrows"))] + for location in self.borrowed_at.iter() { + f.write_fmt(format_args!("\t{}", location))?; + } + Ok(()) + } +} + +impl std::error::Error for AlreadyBorrowedError {} diff --git a/packages/generational-box/src/lib.rs b/packages/generational-box/src/lib.rs index 293bf6c5b..9fe47ba2a 100644 --- a/packages/generational-box/src/lib.rs +++ b/packages/generational-box/src/lib.rs @@ -1,21 +1,25 @@ #![doc = include_str!("../README.md")] #![warn(missing_docs)] -use parking_lot::{ - MappedRwLockReadGuard, MappedRwLockWriteGuard, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard, -}; +use error::*; +use parking_lot::{Mutex, RwLock}; +use references::{GenerationalRefBorrowInfo, GenerationalRefMutBorrowInfo}; use std::any::Any; -use std::error::Error; -use std::fmt::Display; use std::sync::atomic::AtomicU32; use std::{ - cell::{Ref, RefCell, RefMut}, fmt::Debug, marker::PhantomData, ops::{Deref, DerefMut}, - sync::{Arc, OnceLock}, + sync::Arc, }; +use unsync::UnsyncStorage; + +mod error; +mod references; +mod sync; +mod unsync; + /// # Example /// /// ```compile_fail @@ -229,12 +233,15 @@ impl> GenerationalBox { created_at: self.created_at, })); } - self.raw.0.data.try_read().ok_or_else(|| { - BorrowError::Dropped(ValueDroppedError { - #[cfg(any(debug_assertions, feature = "debug_ownership"))] - created_at: self.created_at, - }) - }) + 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, + }, + ) } /// Read the value. Panics if the value is no longer valid. @@ -252,12 +259,14 @@ impl> GenerationalBox { created_at: self.created_at, })); } - self.raw.0.data.try_write().ok_or_else(|| { - BorrowMutError::Dropped(ValueDroppedError { - #[cfg(any(debug_assertions, feature = "debug_ownership"))] - created_at: self.created_at, - }) - }) + 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, + }, + ) } /// Write the value. Panics if the value is no longer valid. @@ -295,25 +304,6 @@ impl Clone for GenerationalBox { } } -/// A unsync storage. This is the default storage type. -pub struct UnsyncStorage(RefCell>>); - -impl Default for UnsyncStorage { - fn default() -> Self { - Self(RefCell::new(None)) - } -} - -/// A thread safe storage. This is slower than the unsync storage, but allows you to share the value between threads. -#[derive(Clone, Copy)] -pub struct SyncStorage(&'static RwLock>>); - -impl Default for SyncStorage { - fn default() -> Self { - Self(Box::leak(Box::new(RwLock::new(None)))) - } -} - /// A trait for types that can be mapped. pub trait Mappable: Deref { /// The type after the mapping. @@ -329,36 +319,6 @@ pub trait Mappable: Deref { ) -> Option>; } -impl Mappable for Ref<'static, T> { - type Mapped = Ref<'static, U>; - - fn map(_self: Self, f: impl FnOnce(&T) -> &U) -> Self::Mapped { - Ref::map(_self, f) - } - - fn try_map( - _self: Self, - f: impl FnOnce(&T) -> Option<&U>, - ) -> Option> { - Ref::filter_map(_self, f).ok() - } -} - -impl Mappable for MappedRwLockReadGuard<'static, T> { - type Mapped = MappedRwLockReadGuard<'static, U>; - - fn map(_self: Self, f: impl FnOnce(&T) -> &U) -> Self::Mapped { - MappedRwLockReadGuard::map(_self, f) - } - - fn try_map( - _self: Self, - f: impl FnOnce(&T) -> Option<&U>, - ) -> Option> { - MappedRwLockReadGuard::try_map(_self, f).ok() - } -} - /// A trait for types that can be mapped mutably. pub trait MappableMut: DerefMut { /// The type after the mapping. @@ -374,36 +334,6 @@ pub trait MappableMut: DerefMut { ) -> Option>; } -impl MappableMut for RefMut<'static, T> { - type Mapped = RefMut<'static, U>; - - fn map(_self: Self, f: impl FnOnce(&mut T) -> &mut U) -> Self::Mapped { - RefMut::map(_self, f) - } - - fn try_map( - _self: Self, - f: impl FnOnce(&mut T) -> Option<&mut U>, - ) -> Option> { - RefMut::filter_map(_self, f).ok() - } -} - -impl MappableMut for MappedRwLockWriteGuard<'static, T> { - type Mapped = MappedRwLockWriteGuard<'static, U>; - - fn map(_self: Self, f: impl FnOnce(&mut T) -> &mut U) -> Self::Mapped { - MappedRwLockWriteGuard::map(_self, f) - } - - fn try_map( - _self: Self, - f: impl FnOnce(&mut T) -> Option<&mut U>, - ) -> Option> { - MappedRwLockWriteGuard::try_map(_self, f).ok() - } -} - /// A trait for a storage backing type. (RefCell, RwLock, etc.) pub trait Storage: AnyStorage + 'static { /// The reference this storage type returns. @@ -412,22 +342,23 @@ pub trait Storage: AnyStorage + 'static { type Mut: MappableMut + DerefMut; /// Try to read the value. Returns None if the value is no longer valid. - fn try_read(&self) -> Option; - /// Read the value. Panics if the value is no longer valid. - fn read(&self) -> Self::Ref { - self.try_read() - .expect("generational box has been invalidated or the type has changed") - } - /// Try to write the value. Returns None if the value is no longer valid. - fn try_write(&self) -> Option; - /// Write the value. Panics if the value is no longer valid. - fn write(&self) -> Self::Mut { - self.try_write() - .expect("generational box has been invalidated or the type has changed") - } + 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; - /// Set the value. Panics if the value is no longer valid. - fn set(&self, value: Data); + /// 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; + + /// Set the value + fn set(&'static self, value: Data); } /// A trait for any storage backing type. @@ -453,127 +384,29 @@ pub trait AnyStorage: Default { } } -impl Storage for UnsyncStorage { - type Ref = Ref<'static, T>; - type Mut = RefMut<'static, T>; - - fn try_read(&self) -> Option { - Ref::filter_map(self.0.borrow(), |any| any.as_ref()?.downcast_ref()).ok() - } - - fn try_write(&self) -> Option { - RefMut::filter_map(self.0.borrow_mut(), |any| any.as_mut()?.downcast_mut()).ok() - } - - fn set(&self, value: T) { - *self.0.borrow_mut() = Some(Box::new(value)); - } -} - -thread_local! { - static UNSYNC_RUNTIME: RefCell>> = RefCell::new(Vec::new()); -} - -impl AnyStorage for UnsyncStorage { - fn data_ptr(&self) -> *const () { - self.0.as_ptr() as *const () - } - - fn take(&self) -> bool { - self.0.borrow_mut().take().is_some() - } - - fn claim() -> MemoryLocation { - UNSYNC_RUNTIME.with(|runtime| { - if let Some(location) = runtime.borrow_mut().pop() { - location - } else { - let data: &'static MemoryLocationInner = - &*Box::leak(Box::new(MemoryLocationInner { - data: Self::default(), - #[cfg(any(debug_assertions, feature = "check_generation"))] - generation: 0.into(), - #[cfg(any(debug_assertions, feature = "debug_borrows"))] - borrowed_at: Default::default(), - #[cfg(any(debug_assertions, feature = "debug_borrows"))] - borrowed_mut_at: Default::default(), - })); - MemoryLocation(data) - } - }) - } - - fn recycle(location: &MemoryLocation) { - location.drop(); - UNSYNC_RUNTIME.with(|runtime| runtime.borrow_mut().push(*location)); - } -} - -impl Storage for SyncStorage { - type Ref = MappedRwLockReadGuard<'static, T>; - type Mut = MappedRwLockWriteGuard<'static, T>; - - fn try_read(&self) -> Option { - RwLockReadGuard::try_map(self.0.read(), |any| any.as_ref()?.downcast_ref()).ok() - } - - fn try_write(&self) -> Option { - RwLockWriteGuard::try_map(self.0.write(), |any| any.as_mut()?.downcast_mut()).ok() - } - - fn set(&self, value: T) { - *self.0.write() = Some(Box::new(value)); - } -} - -static SYNC_RUNTIME: OnceLock>>>> = OnceLock::new(); - -fn sync_runtime() -> &'static Arc>>> { - SYNC_RUNTIME.get_or_init(|| Arc::new(Mutex::new(Vec::new()))) -} - -impl AnyStorage for SyncStorage { - fn data_ptr(&self) -> *const () { - self.0.data_ptr() as *const () - } - - fn take(&self) -> bool { - self.0.write().take().is_some() - } - - fn claim() -> MemoryLocation { - sync_runtime().lock().pop().unwrap_or_else(|| { - let data: &'static MemoryLocationInner = - &*Box::leak(Box::new(MemoryLocationInner { - data: Self::default(), - #[cfg(any(debug_assertions, feature = "check_generation"))] - generation: 0.into(), - #[cfg(any(debug_assertions, feature = "debug_borrows"))] - borrowed_at: Default::default(), - #[cfg(any(debug_assertions, feature = "debug_borrows"))] - borrowed_mut_at: Default::default(), - })); - MemoryLocation(data) - }) - } - - fn recycle(location: &MemoryLocation) { - location.drop(); - sync_runtime().lock().push(*location); - } -} - -#[derive(Clone, Copy)] struct MemoryLocation(&'static MemoryLocationInner); +impl Clone for MemoryLocation { + fn clone(&self) -> Self { + *self + } +} + +impl Copy for MemoryLocation {} + +#[cfg(any(debug_assertions, feature = "debug_borrows"))] +#[derive(Debug, Default)] +struct MemoryLocationBorrowInfo { + borrowed_at: RwLock>>, + borrowed_mut_at: RwLock>>, +} + struct MemoryLocationInner { data: S, #[cfg(any(debug_assertions, feature = "check_generation"))] generation: AtomicU32, #[cfg(any(debug_assertions, feature = "debug_borrows"))] - borrowed_at: RwLock>>, - #[cfg(any(debug_assertions, feature = "debug_borrows"))] - borrowed_mut_at: RwLock>>, + borrow: MemoryLocationBorrowInfo, } impl MemoryLocation { @@ -617,36 +450,29 @@ impl MemoryLocation { &self, #[cfg(any(debug_assertions, feature = "debug_ownership"))] created_at: &'static std::panic::Location<'static>, - ) -> Result, BorrowError> + ) -> std::result::Result<>::Ref, error::BorrowError> where S: Storage, { - #[cfg(any(debug_assertions, feature = "debug_borrows"))] - self.0 - .borrowed_at - .write() - .push(std::panic::Location::caller()); - match self.0.data.try_read() { - Some(borrow) => { - match Ref::filter_map(borrow, |any| any.as_ref()?.downcast_ref::()) { - Ok(reference) => Ok(GenerationalRef { - inner: reference, - #[cfg(any(debug_assertions, feature = "debug_borrows"))] - borrow: GenerationalRefBorrowInfo { - borrowed_at: std::panic::Location::caller(), - borrowed_from: self.0, - }, - }), - Err(_) => Err(BorrowError::Dropped(ValueDroppedError { - #[cfg(any(debug_assertions, feature = "debug_ownership"))] - created_at, - })), - } - } - None => Err(BorrowError::AlreadyBorrowedMut(AlreadyBorrowedMutError { + match self.0.data.try_read( + #[cfg(any(debug_assertions, feature = "debug_ownership"))] + created_at, + #[cfg(any(debug_assertions, feature = "debug_ownership"))] + GenerationalRefBorrowInfo { + borrowed_at: std::panic::Location::caller(), + borrowed_from: &self.0.borrow, + }, + ) { + Ok(read) => { #[cfg(any(debug_assertions, feature = "debug_borrows"))] - borrowed_mut_at: self.0.borrowed_mut_at.read().unwrap(), - })), + self.0 + .borrow + .borrowed_at + .write() + .push(std::panic::Location::caller()); + Ok(read) + } + Err(err) => Err(err), } } @@ -655,270 +481,30 @@ impl MemoryLocation { &self, #[cfg(any(debug_assertions, feature = "debug_ownership"))] created_at: &'static std::panic::Location<'static>, - ) -> Result, BorrowMutError> { - #[cfg(any(debug_assertions, feature = "debug_borrows"))] - { - self.0.borrowed_mut_at.write().unwrap() = Some(std::panic::Location::caller()); - } - match self.0.data.try_borrow_mut() { - Ok(borrow_mut) => { - match RefMut::filter_map(borrow_mut, |any| any.as_mut()?.downcast_mut::()) { - Ok(reference) => Ok(GenerationalRefMut { - inner: reference, - #[cfg(any(debug_assertions, feature = "debug_borrows"))] - borrow: GenerationalRefMutBorrowInfo { - borrowed_from: self.0, - }, - }), - Err(_) => Err(BorrowMutError::Dropped(ValueDroppedError { - #[cfg(any(debug_assertions, feature = "debug_ownership"))] - created_at, - })), + ) -> std::result::Result<>::Mut, error::BorrowMutError> + where + S: Storage, + { + match self.0.data.try_write( + #[cfg(any(debug_assertions, feature = "debug_ownership"))] + created_at, + #[cfg(any(debug_assertions, feature = "debug_ownership"))] + GenerationalRefMutBorrowInfo { + borrowed_from: &self.0.borrow, + }, + ) { + Ok(read) => { + #[cfg(any(debug_assertions, feature = "debug_borrows"))] + { + *self.0.borrow.borrowed_mut_at.write() = Some(std::panic::Location::caller()); } + Ok(read) } - Err(_) => Err(BorrowMutError::AlreadyBorrowed(AlreadyBorrowedError { - #[cfg(any(debug_assertions, feature = "debug_borrows"))] - borrowed_at: self.0.borrowed_at.read().clone(), - })), + Err(err) => Err(err), } } } -#[derive(Debug, Clone)] -/// An error that can occur when trying to borrow a value. -pub enum BorrowError { - /// The value was dropped. - Dropped(ValueDroppedError), - /// The value was already borrowed mutably. - AlreadyBorrowedMut(AlreadyBorrowedMutError), -} - -impl Display for BorrowError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - BorrowError::Dropped(error) => Display::fmt(error, f), - BorrowError::AlreadyBorrowedMut(error) => Display::fmt(error, f), - } - } -} - -impl Error for BorrowError {} - -#[derive(Debug, Clone)] -/// An error that can occur when trying to borrow a value mutably. -pub enum BorrowMutError { - /// The value was dropped. - Dropped(ValueDroppedError), - /// The value was already borrowed. - AlreadyBorrowed(AlreadyBorrowedError), - /// The value was already borrowed mutably. - AlreadyBorrowedMut(AlreadyBorrowedMutError), -} - -impl Display for BorrowMutError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - BorrowMutError::Dropped(error) => Display::fmt(error, f), - BorrowMutError::AlreadyBorrowedMut(error) => Display::fmt(error, f), - BorrowMutError::AlreadyBorrowed(error) => Display::fmt(error, f), - } - } -} - -impl Error for BorrowMutError {} - -/// An error that can occur when trying to use a value that has been dropped. -#[derive(Debug, Copy, Clone)] -pub struct ValueDroppedError { - #[cfg(any(debug_assertions, feature = "debug_ownership"))] - created_at: &'static std::panic::Location<'static>, -} - -impl Display for ValueDroppedError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str("Failed to borrow because the value was dropped.")?; - #[cfg(any(debug_assertions, feature = "debug_ownership"))] - f.write_fmt(format_args!("created_at: {}", self.created_at))?; - Ok(()) - } -} - -impl std::error::Error for ValueDroppedError {} - -/// An error that can occur when trying to borrow a value that has already been borrowed mutably. -#[derive(Debug, Copy, Clone)] -pub struct AlreadyBorrowedMutError { - #[cfg(any(debug_assertions, feature = "debug_borrows"))] - borrowed_mut_at: &'static std::panic::Location<'static>, -} - -impl Display for AlreadyBorrowedMutError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str("Failed to borrow because the value was already borrowed mutably.")?; - #[cfg(any(debug_assertions, feature = "debug_borrows"))] - f.write_fmt(format_args!("borrowed_mut_at: {}", self.borrowed_mut_at))?; - Ok(()) - } -} - -impl std::error::Error for AlreadyBorrowedMutError {} - -/// An error that can occur when trying to borrow a value mutably that has already been borrowed immutably. -#[derive(Debug, Clone)] -pub struct AlreadyBorrowedError { - #[cfg(any(debug_assertions, feature = "debug_borrows"))] - borrowed_at: Vec<&'static std::panic::Location<'static>>, -} - -impl Display for AlreadyBorrowedError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str("Failed to borrow mutably because the value was already borrowed immutably.")?; - #[cfg(any(debug_assertions, feature = "debug_borrows"))] - f.write_str("borrowed_at:")?; - #[cfg(any(debug_assertions, feature = "debug_borrows"))] - for location in self.borrowed_at.iter() { - f.write_fmt(format_args!("\t{}", location))?; - } - Ok(()) - } -} - -impl std::error::Error for AlreadyBorrowedError {} - -/// A reference to a value in a generational box. -pub struct GenerationalRef { - inner: Ref<'static, T>, - #[cfg(any(debug_assertions, feature = "debug_borrows"))] - borrow: GenerationalRefBorrowInfo, -} - -impl GenerationalRef { - /// Map one ref type to another. - pub fn map(orig: GenerationalRef, f: F) -> GenerationalRef - where - F: FnOnce(&T) -> &U, - { - GenerationalRef { - inner: Ref::map(orig.inner, f), - #[cfg(any(debug_assertions, feature = "debug_borrows"))] - borrow: GenerationalRefBorrowInfo { - borrowed_at: orig.borrow.borrowed_at, - borrowed_from: orig.borrow.borrowed_from, - }, - } - } - - /// Filter one ref type to another. - pub fn filter_map(orig: GenerationalRef, f: F) -> Option> - where - F: FnOnce(&T) -> Option<&U>, - { - let Self { - inner, - #[cfg(any(debug_assertions, feature = "debug_borrows"))] - borrow, - } = orig; - Ref::filter_map(inner, f).ok().map(|inner| GenerationalRef { - inner, - #[cfg(any(debug_assertions, feature = "debug_borrows"))] - borrow: GenerationalRefBorrowInfo { - borrowed_at: borrow.borrowed_at, - borrowed_from: borrow.borrowed_from, - }, - }) - } -} - -impl Deref for GenerationalRef { - type Target = T; - - fn deref(&self) -> &Self::Target { - self.inner.deref() - } -} - -#[cfg(any(debug_assertions, feature = "debug_borrows"))] -struct GenerationalRefBorrowInfo { - borrowed_at: &'static std::panic::Location<'static>, - borrowed_from: &'static MemoryLocationInner, -} - -#[cfg(any(debug_assertions, feature = "debug_borrows"))] -impl Drop for GenerationalRefBorrowInfo { - fn drop(&mut self) { - self.borrowed_from - .borrowed_at - .borrow_mut() - .retain(|location| std::ptr::eq(*location, self.borrowed_at as *const _)); - } -} - -/// A mutable reference to a value in a generational box. -pub struct GenerationalRefMut { - inner: RefMut<'static, T>, - #[cfg(any(debug_assertions, feature = "debug_borrows"))] - borrow: GenerationalRefMutBorrowInfo, -} - -impl GenerationalRefMut { - /// Map one ref type to another. - pub fn map(orig: GenerationalRefMut, f: F) -> GenerationalRefMut - where - F: FnOnce(&mut T) -> &mut U, - { - GenerationalRefMut { - inner: RefMut::map(orig.inner, f), - #[cfg(any(debug_assertions, feature = "debug_borrows"))] - borrow: orig.borrow, - } - } - - /// Filter one ref type to another. - pub fn filter_map(orig: GenerationalRefMut, f: F) -> Option> - where - F: FnOnce(&mut T) -> Option<&mut U>, - { - let Self { - inner, - #[cfg(any(debug_assertions, feature = "debug_borrows"))] - borrow, - } = orig; - RefMut::filter_map(inner, f) - .ok() - .map(|inner| GenerationalRefMut { - inner, - #[cfg(any(debug_assertions, feature = "debug_borrows"))] - borrow, - }) - } -} - -impl Deref for GenerationalRefMut { - type Target = T; - - fn deref(&self) -> &Self::Target { - self.inner.deref() - } -} - -impl DerefMut for GenerationalRefMut { - fn deref_mut(&mut self) -> &mut Self::Target { - self.inner.deref_mut() - } -} - -#[cfg(any(debug_assertions, feature = "debug_borrows"))] -struct GenerationalRefMutBorrowInfo { - borrowed_from: &'static MemoryLocationInner, -} - -#[cfg(any(debug_assertions, feature = "debug_borrows"))] -impl Drop for GenerationalRefMutBorrowInfo { - fn drop(&mut self) { - self.borrowed_from.borrowed_mut_at.take(); - } -} - /// Owner: Handles dropping generational boxes. The owner acts like a runtime lifetime guard. Any states that you create with an owner will be dropped when that owner is dropped. pub struct Owner { owned: Arc>>>, @@ -926,36 +512,23 @@ pub struct Owner { } impl Owner { - /// Insert a value into the store. The value will be dropped when the owner is dropped. - #[track_caller] - pub fn insert(&self, value: T) -> GenerationalBox + /// Insert a value into the store with a specific location blamed for creating the value. The value will be dropped when the owner is dropped. + pub fn insert_with_caller( + &self, + value: T, + #[cfg(any(debug_assertions, feature = "debug_ownership"))] + caller: &'static std::panic::Location<'static>, + ) -> GenerationalBox where S: Storage, { let mut location = S::claim(); - let key = location.replace_with_caller( - value, - #[cfg(any(debug_assertions, feature = "debug_borrows"))] - std::panic::Location::caller(), - ); - self.owned.lock().push(location); - key - } - - /// Insert a value into the store with a specific location blamed for creating the value. The value will be dropped when the owner is dropped. - pub fn insert_with_caller( - &self, - value: T, - #[cfg(any(debug_assertions, feature = "debug_ownership"))] - caller: &'static std::panic::Location<'static>, - ) -> GenerationalBox { - let mut location = self.store.claim(); let key = location.replace_with_caller( value, #[cfg(any(debug_assertions, feature = "debug_borrows"))] caller, ); - self.owned.borrow_mut().push(location); + self.owned.lock().push(location); key } diff --git a/packages/generational-box/src/references.rs b/packages/generational-box/src/references.rs new file mode 100644 index 000000000..013541bd7 --- /dev/null +++ b/packages/generational-box/src/references.rs @@ -0,0 +1,169 @@ +use std::{ + marker::PhantomData, + ops::{Deref, DerefMut}, +}; + +use crate::{Mappable, MappableMut, MemoryLocationBorrowInfo}; + +/// A reference to a value in a generational box. +pub struct GenerationalRef> { + inner: R, + phantom: PhantomData, + #[cfg(any(debug_assertions, feature = "debug_borrows"))] + borrow: GenerationalRefBorrowInfo, +} + +impl> GenerationalRef { + pub(crate) fn new( + inner: R, + #[cfg(any(debug_assertions, feature = "debug_borrows"))] borrow: GenerationalRefBorrowInfo, + ) -> Self { + Self { + inner, + phantom: PhantomData, + #[cfg(any(debug_assertions, feature = "debug_borrows"))] + borrow, + } + } +} + +impl> Mappable for GenerationalRef { + type Mapped = GenerationalRef>; + + fn map(_self: Self, f: impl FnOnce(&T) -> &U) -> Self::Mapped { + GenerationalRef { + inner: R::map(_self.inner, f), + phantom: PhantomData, + #[cfg(any(debug_assertions, feature = "debug_borrows"))] + borrow: GenerationalRefBorrowInfo { + borrowed_at: _self.borrow.borrowed_at, + borrowed_from: _self.borrow.borrowed_from, + }, + } + } + + fn try_map( + _self: Self, + f: impl FnOnce(&T) -> Option<&U>, + ) -> Option> { + let Self { + inner, + #[cfg(any(debug_assertions, feature = "debug_borrows"))] + borrow, + .. + } = _self; + R::try_map(inner, f).map(|inner| GenerationalRef { + inner, + phantom: PhantomData, + #[cfg(any(debug_assertions, feature = "debug_borrows"))] + borrow: GenerationalRefBorrowInfo { + borrowed_at: borrow.borrowed_at, + borrowed_from: borrow.borrowed_from, + }, + }) + } +} + +impl> Deref for GenerationalRef { + type Target = T; + + fn deref(&self) -> &Self::Target { + self.inner.deref() + } +} + +#[cfg(any(debug_assertions, feature = "debug_borrows"))] +pub(crate) struct GenerationalRefBorrowInfo { + pub(crate) borrowed_at: &'static std::panic::Location<'static>, + pub(crate) borrowed_from: &'static MemoryLocationBorrowInfo, +} + +#[cfg(any(debug_assertions, feature = "debug_borrows"))] +impl Drop for GenerationalRefBorrowInfo { + fn drop(&mut self) { + self.borrowed_from + .borrowed_at + .write() + .retain(|location| std::ptr::eq(*location, self.borrowed_at as *const _)); + } +} + +/// A mutable reference to a value in a generational box. +pub struct GenerationalRefMut> { + inner: W, + phantom: PhantomData, + #[cfg(any(debug_assertions, feature = "debug_borrows"))] + borrow: GenerationalRefMutBorrowInfo, +} + +impl> GenerationalRefMut { + pub(crate) fn new( + inner: R, + #[cfg(any(debug_assertions, feature = "debug_borrows"))] + borrow: GenerationalRefMutBorrowInfo, + ) -> Self { + Self { + inner, + phantom: PhantomData, + #[cfg(any(debug_assertions, feature = "debug_borrows"))] + borrow, + } + } +} + +impl> MappableMut for GenerationalRefMut { + type Mapped = GenerationalRefMut>; + + fn map(_self: Self, f: impl FnOnce(&mut T) -> &mut U) -> Self::Mapped { + GenerationalRefMut { + inner: W::map(_self.inner, f), + phantom: PhantomData, + #[cfg(any(debug_assertions, feature = "debug_borrows"))] + borrow: _self.borrow, + } + } + + fn try_map( + _self: Self, + f: impl FnOnce(&mut T) -> Option<&mut U>, + ) -> Option> { + let Self { + inner, + #[cfg(any(debug_assertions, feature = "debug_borrows"))] + borrow, + .. + } = _self; + W::try_map(inner, f).map(|inner| GenerationalRefMut { + inner, + phantom: PhantomData, + #[cfg(any(debug_assertions, feature = "debug_borrows"))] + borrow, + }) + } +} + +impl> Deref for GenerationalRefMut { + type Target = T; + + fn deref(&self) -> &Self::Target { + self.inner.deref() + } +} + +impl> DerefMut for GenerationalRefMut { + fn deref_mut(&mut self) -> &mut Self::Target { + self.inner.deref_mut() + } +} + +#[cfg(any(debug_assertions, feature = "debug_borrows"))] +pub(crate) struct GenerationalRefMutBorrowInfo { + pub(crate) borrowed_from: &'static MemoryLocationBorrowInfo, +} + +#[cfg(any(debug_assertions, feature = "debug_borrows"))] +impl Drop for GenerationalRefMutBorrowInfo { + fn drop(&mut self) { + self.borrowed_from.borrowed_mut_at.write().take(); + } +} diff --git a/packages/generational-box/src/sync.rs b/packages/generational-box/src/sync.rs new file mode 100644 index 000000000..1432ca6d8 --- /dev/null +++ b/packages/generational-box/src/sync.rs @@ -0,0 +1,135 @@ +use parking_lot::{ + MappedRwLockReadGuard, MappedRwLockWriteGuard, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard, +}; +use std::sync::{Arc, OnceLock}; + +use crate::{ + error::{self, ValueDroppedError}, + references::{ + GenerationalRef, GenerationalRefBorrowInfo, GenerationalRefMut, + GenerationalRefMutBorrowInfo, + }, + AnyStorage, Mappable, MappableMut, MemoryLocation, MemoryLocationInner, Storage, +}; + +/// A thread safe storage. This is slower than the unsync storage, but allows you to share the value between threads. +#[derive(Default)] +pub struct SyncStorage(RwLock>>); + +static SYNC_RUNTIME: OnceLock>>>> = OnceLock::new(); + +fn sync_runtime() -> &'static Arc>>> { + SYNC_RUNTIME.get_or_init(|| Arc::new(Mutex::new(Vec::new()))) +} + +impl AnyStorage for SyncStorage { + fn data_ptr(&self) -> *const () { + self.0.data_ptr() as *const () + } + + fn take(&self) -> bool { + self.0.write().take().is_some() + } + + fn claim() -> MemoryLocation { + sync_runtime().lock().pop().unwrap_or_else(|| { + let data: &'static MemoryLocationInner = + &*Box::leak(Box::new(MemoryLocationInner { + data: Self::default(), + #[cfg(any(debug_assertions, feature = "check_generation"))] + generation: 0.into(), + #[cfg(any(debug_assertions, feature = "debug_borrows"))] + borrow: Default::default(), + })); + MemoryLocation(data) + }) + } + + fn recycle(location: &MemoryLocation) { + location.drop(); + sync_runtime().lock().push(*location); + } +} + +impl Mappable for MappedRwLockReadGuard<'static, T> { + type Mapped = MappedRwLockReadGuard<'static, U>; + + fn map(_self: Self, f: impl FnOnce(&T) -> &U) -> Self::Mapped { + MappedRwLockReadGuard::map(_self, f) + } + + fn try_map( + _self: Self, + f: impl FnOnce(&T) -> Option<&U>, + ) -> Option> { + MappedRwLockReadGuard::try_map(_self, f).ok() + } +} + +impl MappableMut for MappedRwLockWriteGuard<'static, T> { + type Mapped = MappedRwLockWriteGuard<'static, U>; + + fn map(_self: Self, f: impl FnOnce(&mut T) -> &mut U) -> Self::Mapped { + MappedRwLockWriteGuard::map(_self, f) + } + + fn try_map( + _self: Self, + f: impl FnOnce(&mut T) -> Option<&mut U>, + ) -> Option> { + MappedRwLockWriteGuard::try_map(_self, f).ok() + } +} + +impl Storage for SyncStorage { + type Ref = GenerationalRef>; + type Mut = GenerationalRefMut>; + + 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 { + RwLockReadGuard::try_map(self.0.read(), |any| any.as_ref()?.downcast_ref()) + .map_err(|_| { + error::BorrowError::Dropped(ValueDroppedError { + #[cfg(any(debug_assertions, feature = "debug_ownership"))] + created_at, + }) + }) + .map(|guard| { + GenerationalRef::new( + guard, + #[cfg(any(debug_assertions, feature = "debug_ownership"))] + at, + ) + }) + } + + 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 { + RwLockWriteGuard::try_map(self.0.write(), |any| any.as_mut()?.downcast_mut()) + .map_err(|_| { + error::BorrowMutError::Dropped(ValueDroppedError { + #[cfg(any(debug_assertions, feature = "debug_ownership"))] + created_at, + }) + }) + .map(|guard| { + GenerationalRefMut::new( + guard, + #[cfg(any(debug_assertions, feature = "debug_ownership"))] + at, + ) + }) + } + + fn set(&self, value: T) { + *self.0.write() = Some(Box::new(value)); + } +} diff --git a/packages/generational-box/src/unsync.rs b/packages/generational-box/src/unsync.rs new file mode 100644 index 000000000..83c670480 --- /dev/null +++ b/packages/generational-box/src/unsync.rs @@ -0,0 +1,137 @@ +use crate::{ + references::{ + GenerationalRef, GenerationalRefBorrowInfo, GenerationalRefMut, + GenerationalRefMutBorrowInfo, + }, + AnyStorage, Mappable, MappableMut, MemoryLocation, MemoryLocationInner, Storage, +}; +use std::cell::{Ref, RefCell, RefMut}; + +/// A unsync storage. This is the default storage type. +pub struct UnsyncStorage(RefCell>>); + +impl Default for UnsyncStorage { + fn default() -> Self { + Self(RefCell::new(None)) + } +} + +impl Mappable for Ref<'static, T> { + type Mapped = Ref<'static, U>; + + fn map(_self: Self, f: impl FnOnce(&T) -> &U) -> Self::Mapped { + Ref::map(_self, f) + } + + fn try_map( + _self: Self, + f: impl FnOnce(&T) -> Option<&U>, + ) -> Option> { + Ref::filter_map(_self, f).ok() + } +} + +impl MappableMut for RefMut<'static, T> { + type Mapped = RefMut<'static, U>; + + fn map(_self: Self, f: impl FnOnce(&mut T) -> &mut U) -> Self::Mapped { + RefMut::map(_self, f) + } + + fn try_map( + _self: Self, + f: impl FnOnce(&mut T) -> Option<&mut U>, + ) -> Option> { + RefMut::filter_map(_self, f).ok() + } +} + +impl Storage for UnsyncStorage { + type Ref = GenerationalRef>; + type Mut = GenerationalRefMut>; + + 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 { + Ref::filter_map(self.0.borrow(), |any| any.as_ref()?.downcast_ref()) + .map_err(|_| { + crate::error::BorrowError::Dropped( + #[cfg(any(debug_assertions, feature = "debug_ownership"))] + crate::error::ValueDroppedError { created_at }, + ) + }) + .map(|guard| { + GenerationalRef::new( + guard, + #[cfg(any(debug_assertions, feature = "debug_ownership"))] + at, + ) + }) + } + + 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 { + RefMut::filter_map(self.0.borrow_mut(), |any| any.as_mut()?.downcast_mut()) + .map_err(|_| { + crate::error::BorrowMutError::Dropped( + #[cfg(any(debug_assertions, feature = "debug_ownership"))] + crate::error::ValueDroppedError { created_at }, + ) + }) + .map(|guard| { + GenerationalRefMut::new( + guard, + #[cfg(any(debug_assertions, feature = "debug_ownership"))] + at, + ) + }) + } + + fn set(&self, value: T) { + *self.0.borrow_mut() = Some(Box::new(value)); + } +} + +thread_local! { + static UNSYNC_RUNTIME: RefCell>> = RefCell::new(Vec::new()); +} + +impl AnyStorage for UnsyncStorage { + fn data_ptr(&self) -> *const () { + self.0.as_ptr() as *const () + } + + fn take(&self) -> bool { + self.0.borrow_mut().take().is_some() + } + + fn claim() -> MemoryLocation { + UNSYNC_RUNTIME.with(|runtime| { + if let Some(location) = runtime.borrow_mut().pop() { + location + } else { + let data: &'static MemoryLocationInner = + &*Box::leak(Box::new(MemoryLocationInner { + data: Self::default(), + #[cfg(any(debug_assertions, feature = "check_generation"))] + generation: 0.into(), + #[cfg(any(debug_assertions, feature = "debug_borrows"))] + borrow: Default::default(), + })); + MemoryLocation(data) + } + }) + } + + fn recycle(location: &MemoryLocation) { + location.drop(); + UNSYNC_RUNTIME.with(|runtime| runtime.borrow_mut().push(*location)); + } +} diff --git a/packages/signals/src/signal.rs b/packages/signals/src/signal.rs index 8feb7fe95..d99807f0a 100644 --- a/packages/signals/src/signal.rs +++ b/packages/signals/src/signal.rs @@ -235,10 +235,9 @@ impl>> Signal { inner: CopyValue::new_with_caller( SignalData { subscribers: Default::default(), - effect_subscribers: Default::default(), update_any: schedule_update_any().expect("in a virtual dom"), value, - effect_stack: get_effect_stack(), + effect_ref: get_effect_ref(), }, #[cfg(debug_assertions)] caller,