diff --git a/packages/generational-box/benches/lock.rs b/packages/generational-box/benches/lock.rs index fa3954fbb..14a94dd56 100644 --- a/packages/generational-box/benches/lock.rs +++ b/packages/generational-box/benches/lock.rs @@ -1,23 +1,32 @@ #![allow(unused)] -use generational_box::{GenerationalBox, Owner, Store}; +use generational_box::*; use criterion::{black_box, criterion_group, criterion_main, Criterion}; -fn create(owner: &Owner) -> GenerationalBox { +fn create>(owner: &Owner) -> GenerationalBox { owner.insert(0) } -fn set_read(signal: GenerationalBox) -> u32 { +fn set_read>(signal: GenerationalBox) -> u32 { signal.set(1); *signal.read() } fn bench_fib(c: &mut Criterion) { - let store = Store::default(); - let owner = store.owner(); - c.bench_function("create", |b| b.iter(|| create(black_box(&owner)))); - let signal = create(&owner); - c.bench_function("set_read", |b| b.iter(|| set_read(black_box(signal)))); + { + let owner = UnsyncStorage::owner(); + c.bench_function("create_unsync", |b| b.iter(|| create(black_box(&owner)))); + let signal = create(&owner); + c.bench_function("set_read_unsync", |b| { + b.iter(|| set_read(black_box(signal))) + }); + } + { + let owner = SyncStorage::owner(); + c.bench_function("create_sync", |b| b.iter(|| create(black_box(&owner)))); + let signal = create(&owner); + c.bench_function("set_read_sync", |b| b.iter(|| set_read(black_box(signal)))); + } } criterion_group!(benches, bench_fib); diff --git a/packages/generational-box/src/lib.rs b/packages/generational-box/src/lib.rs index 5d4866d05..ec1986f39 100644 --- a/packages/generational-box/src/lib.rs +++ b/packages/generational-box/src/lib.rs @@ -5,21 +5,19 @@ use parking_lot::{ MappedRwLockReadGuard, MappedRwLockWriteGuard, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard, }; use std::{ - cell::RefCell, + cell::{Ref, RefCell, RefMut}, fmt::Debug, marker::PhantomData, + ops::{Deref, DerefMut}, rc::Rc, - sync::{atomic::AtomicU32, Arc}, + sync::{atomic::AtomicU32, Arc, OnceLock}, }; -mod testing; - /// # Example /// /// ```compile_fail /// let data = String::from("hello world"); -/// let store = Store::default(); -/// let owner = store.owner(); +/// let owner = UnsyncStorage::owner(); /// let key = owner.insert(&data); /// drop(data); /// assert_eq!(*key.read(), "hello world"); @@ -29,15 +27,14 @@ fn compile_fail() {} #[test] fn reused() { - let store = Store::default(); let first_ptr; { - let owner = store.owner(); + let owner = UnsyncStorage::owner(); first_ptr = owner.insert(1).raw.data.data_ptr(); drop(owner); } { - let owner = store.owner(); + let owner = UnsyncStorage::owner(); let second_ptr = owner.insert(1234).raw.data.data_ptr(); assert_eq!(first_ptr, second_ptr); drop(owner); @@ -47,11 +44,10 @@ fn reused() { #[test] fn leaking_is_ok() { let data = String::from("hello world"); - let store = Store::default(); let key; { // create an owner - let owner = store.owner(); + let owner = UnsyncStorage::owner(); // insert data into the store key = owner.insert(data); // don't drop the owner @@ -63,11 +59,10 @@ fn leaking_is_ok() { #[test] fn drops() { let data = String::from("hello world"); - let store = Store::default(); let key; { // create an owner - let owner = store.owner(); + let owner = UnsyncStorage::owner(); // insert data into the store key = owner.insert(data); // drop the owner @@ -77,8 +72,7 @@ fn drops() { #[test] fn works() { - let store = Store::default(); - let owner = store.owner(); + let owner = UnsyncStorage::owner(); let key = owner.insert(1); assert_eq!(*key.read(), 1); @@ -86,8 +80,7 @@ fn works() { #[test] fn insert_while_reading() { - let store = Store::default(); - let owner = store.owner(); + let owner = UnsyncStorage::owner(); let key; { let data: String = "hello world".to_string(); @@ -101,8 +94,7 @@ fn insert_while_reading() { #[test] #[should_panic] fn panics() { - let store = Store::default(); - let owner = store.owner(); + let owner = UnsyncStorage::owner(); let key = owner.insert(1); drop(owner); @@ -112,7 +104,6 @@ fn panics() { #[test] fn fuzz() { fn maybe_owner_scope( - store: &Store, valid_keys: &mut Vec>, invalid_keys: &mut Vec>, path: &mut Vec, @@ -125,7 +116,7 @@ fn fuzz() { }; for i in 0..children { - let owner = store.owner(); + let owner = UnsyncStorage::owner(); let key = owner.insert(format!("hello world {path:?}")); valid_keys.push(key); path.push(i); @@ -140,27 +131,26 @@ fn fuzz() { for key in invalid_keys.iter() { assert!(!key.validate()); } - maybe_owner_scope(store, valid_keys, invalid_keys, path); + maybe_owner_scope(valid_keys, invalid_keys, path); invalid_keys.push(valid_keys.pop().unwrap()); path.pop(); } } for _ in 0..10 { - let store = Store::default(); - maybe_owner_scope(&store, &mut Vec::new(), &mut Vec::new(), &mut Vec::new()); + maybe_owner_scope(&mut Vec::new(), &mut Vec::new(), &mut Vec::new()); } } /// The core Copy state type. The generational box will be dropped when the [Owner] is dropped. -pub struct GenerationalBox { - raw: MemoryLocation, +pub struct GenerationalBox { + raw: MemoryLocation, #[cfg(any(debug_assertions, feature = "check_generation"))] generation: u32, _marker: PhantomData, } -impl Debug for GenerationalBox { +impl Debug for GenerationalBox { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { #[cfg(any(debug_assertions, feature = "check_generation"))] f.write_fmt(format_args!( @@ -174,7 +164,7 @@ impl Debug for GenerationalBox { } } -impl GenerationalBox { +impl> GenerationalBox { #[inline(always)] fn validate(&self) -> bool { #[cfg(any(debug_assertions, feature = "check_generation"))] @@ -191,43 +181,29 @@ impl GenerationalBox { } /// Try to read the value. Returns None if the value is no longer valid. - pub fn try_read(&self) -> Option> { - self.validate() - .then(|| { - RwLockReadGuard::try_map(self.raw.data.read(), |any| { - any.as_ref()?.downcast_ref::() - }) - .ok() - }) - .flatten() + pub fn try_read(&self) -> Option { + self.validate().then(|| self.raw.data.try_read()).flatten() } /// Read the value. Panics if the value is no longer valid. - pub fn read(&self) -> MappedRwLockReadGuard<'static, T> { + pub fn read(&self) -> S::Ref { self.try_read().unwrap() } /// Try to write the value. Returns None if the value is no longer valid. - pub fn try_write(&self) -> Option> { - self.validate() - .then(|| { - RwLockWriteGuard::try_map(self.raw.data.write(), |any| { - any.as_mut()?.downcast_mut::() - }) - .ok() - }) - .flatten() + pub fn try_write(&self) -> Option where { + self.validate().then(|| self.raw.data.try_write()).flatten() } /// Write the value. Panics if the value is no longer valid. - pub fn write(&self) -> MappedRwLockWriteGuard<'static, T> { + pub fn write(&self) -> S::Mut { self.try_write().unwrap() } /// Set the value. Panics if the value is no longer valid. pub fn set(&self, value: T) { self.validate().then(|| { - *self.raw.data.write() = Some(Box::new(value)); + self.raw.data.set(value); }); } @@ -245,40 +221,252 @@ impl GenerationalBox { } } -impl Copy for GenerationalBox {} +impl Copy for GenerationalBox {} -impl Clone for GenerationalBox { +impl Clone for GenerationalBox { fn clone(&self) -> Self { *self } } #[derive(Clone, Copy)] -struct MemoryLocation { - data: &'static RwLock>>, +pub struct UnsyncStorage(&'static RefCell>>); + +impl Default for UnsyncStorage { + fn default() -> Self { + Self(Box::leak(Box::new(RefCell::new(None)))) + } +} + +#[derive(Clone, Copy)] +pub struct SyncStorage(&'static RwLock>>); + +impl Default for SyncStorage { + fn default() -> Self { + Self(Box::leak(Box::new(RwLock::new(None)))) + } +} + +pub trait Mappable: Deref { + type Mapped: Deref; + + fn map(_self: Self, f: fn(&T) -> &U) -> Self::Mapped; +} + +impl Mappable for Ref<'static, T> { + type Mapped = Ref<'static, U>; + + fn map(_self: Self, f: fn(&T) -> &U) -> Self::Mapped { + Ref::map(_self, f) + } +} + +impl Mappable for MappedRwLockReadGuard<'static, T> { + type Mapped = MappedRwLockReadGuard<'static, U>; + + fn map(_self: Self, f: fn(&T) -> &U) -> Self::Mapped { + MappedRwLockReadGuard::map(_self, f) + } +} + +pub trait MappableMut: DerefMut { + type Mapped: DerefMut; + + fn map(_self: Self, f: fn(&mut T) -> &mut U) -> Self::Mapped; +} + +impl MappableMut for RefMut<'static, T> { + type Mapped = RefMut<'static, U>; + + fn map(_self: Self, f: fn(&mut T) -> &mut U) -> Self::Mapped { + RefMut::map(_self, f) + } +} + +impl MappableMut for MappedRwLockWriteGuard<'static, T> { + type Mapped = MappedRwLockWriteGuard<'static, U>; + + fn map(_self: Self, f: fn(&mut T) -> &mut U) -> Self::Mapped { + MappedRwLockWriteGuard::map(_self, f) + } +} + +pub trait Storage: Copy + AnyStorage { + type Ref: Deref; + type Mut: DerefMut; + + fn try_read(&self) -> Option; + fn read(&self) -> Self::Ref { + self.try_read() + .expect("generational box has been invalidated or the type has changed") + } + fn try_write(&self) -> Option; + fn write(&self) -> Self::Mut { + self.try_write() + .expect("generational box has been invalidated or the type has changed") + } + + fn set(&self, value: Data); +} + +pub trait AnyStorage: Default { + fn data_ptr(&self) -> *const (); + + fn take(&self) -> bool; + + fn recycle(location: &MemoryLocation); + // { + // location.drop(); + // self.recycled.lock().push(location); + // } + + fn claim() -> MemoryLocation; + // where + // S: Default, + // { + // if let Some(location) = self.recycled.lock().pop() { + // location + // } else { + // MemoryLocation { + // data: Default::default(), + // #[cfg(any(debug_assertions, feature = "check_generation"))] + // generation: Box::leak(Box::new(Default::default())), + // } + // } + // } + + /// Create a new owner. The owner will be responsible for dropping all of the generational boxes that it creates. + fn owner() -> Owner { + Owner { + owned: Default::default(), + phantom: PhantomData, + } + } +} + +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 { + MemoryLocation { + data: UnsyncStorage(Box::leak(Box::new(RefCell::new(None)))), + #[cfg(any(debug_assertions, feature = "check_generation"))] + generation: Box::leak(Box::new(Default::default())), + } + } + }) + } + + 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 { + MemoryLocation { + data: SyncStorage(Box::leak(Box::new(RwLock::new(None)))), + #[cfg(any(debug_assertions, feature = "check_generation"))] + generation: Box::leak(Box::new(Default::default())), + } + } + + fn recycle(location: &MemoryLocation) { + location.drop(); + sync_runtime().lock().push(*location); + } +} + +#[derive(Clone, Copy)] +struct MemoryLocation { + data: S, #[cfg(any(debug_assertions, feature = "check_generation"))] generation: &'static AtomicU32, } -impl MemoryLocation { +impl MemoryLocation { #[allow(unused)] - fn drop(&self) { - let old = self.data.write().take(); + fn drop(&self) + where + S: AnyStorage, + { + let old = self.data.take(); #[cfg(any(debug_assertions, feature = "check_generation"))] - if old.is_some() { - drop(old); + if old { let new_generation = self.generation.load(std::sync::atomic::Ordering::Relaxed) + 1; self.generation .store(new_generation, std::sync::atomic::Ordering::Relaxed); } } - fn replace(&mut self, value: T) -> GenerationalBox { - let mut inner_mut = self.data.write(); - - let raw = Box::new(value); - let old = inner_mut.replace(raw); - assert!(old.is_none()); + fn replace(&mut self, value: T) -> GenerationalBox + where + S: Storage + Copy, + { + self.data.set(value); GenerationalBox { raw: *self, #[cfg(any(debug_assertions, feature = "check_generation"))] @@ -288,66 +476,27 @@ impl MemoryLocation { } } -/// Handles recycling generational boxes that have been dropped. Your application should have one store or one store per thread. -#[derive(Clone)] -pub struct Store { - recycled: Arc>>, -} - -impl Default for Store { - fn default() -> Self { - Self { - recycled: Default::default(), - } - } -} - -impl Store { - fn recycle(&self, location: MemoryLocation) { - location.drop(); - self.recycled.lock().push(location); - } - - fn claim(&self) -> MemoryLocation { - if let Some(location) = self.recycled.lock().pop() { - location - } else { - let data: &'static RwLock<_> = Box::leak(Box::new(RwLock::new(None))); - MemoryLocation { - data, - #[cfg(any(debug_assertions, feature = "check_generation"))] - generation: Box::leak(Box::new(Default::default())), - } - } - } - - /// Create a new owner. The owner will be responsible for dropping all of the generational boxes that it creates. - pub fn owner(&self) -> Owner { - Owner { - store: self.clone(), - owned: Default::default(), - } - } -} - /// 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 { - store: Store, - owned: Rc>>, +pub struct Owner { + owned: Arc>>>, + phantom: PhantomData, } -impl Owner { +impl Owner { /// Insert a value into the store. The value will be dropped when the owner is dropped. - pub fn insert(&self, value: T) -> GenerationalBox { - let mut location = self.store.claim(); + pub fn insert(&self, value: T) -> GenerationalBox + where + S: Storage, + { + let mut location = S::claim(); let key = location.replace(value); - self.owned.borrow_mut().push(location); + self.owned.lock().push(location); key } /// Creates an invalid handle. This is useful for creating a handle that will be filled in later. If you use this before the value is filled in, you will get may get a panic or an out of date value. - pub fn invalid(&self) -> GenerationalBox { - let location = self.store.claim(); + pub fn invalid(&self) -> GenerationalBox { + let location = S::claim(); GenerationalBox { raw: location, #[cfg(any(debug_assertions, feature = "check_generation"))] @@ -359,10 +508,10 @@ impl Owner { } } -impl Drop for Owner { +impl Drop for Owner { fn drop(&mut self) { - for location in self.owned.borrow().iter() { - self.store.recycle(*location) + for location in self.owned.lock().iter() { + S::recycle(location) } } } diff --git a/packages/generational-box/src/testing.rs b/packages/generational-box/src/testing.rs deleted file mode 100644 index 0f8db53dd..000000000 --- a/packages/generational-box/src/testing.rs +++ /dev/null @@ -1,44 +0,0 @@ -use core::marker::PhantomData; - -#[test] -fn testing() { - let testing1 = Testing::<_, false>::new(&() as *const _); - // std::thread::spawn(move || testing1); -} - -struct Testing { - ptr: *mut (), - phantom: PhantomData, -} - -trait CreateNew { - fn new(data: T) -> Testing { - Testing { - ptr: &mut (), - phantom: PhantomData, - } - } -} - -impl CreateNew for Testing { - fn new(data: T) -> Testing { - Testing { - ptr: &mut (), - phantom: PhantomData, - } - } -} - -impl Testing { - pub fn new(data: T) -> Self { - Testing { - ptr: &mut (), - phantom: PhantomData, - } - } -} - -impl Testing {} - -unsafe impl Send for Testing {} -unsafe impl Sync for Testing {} diff --git a/packages/signals/src/effect.rs b/packages/signals/src/effect.rs index 307ee98ab..2ab29780d 100644 --- a/packages/signals/src/effect.rs +++ b/packages/signals/src/effect.rs @@ -2,13 +2,14 @@ use core::{self, fmt::Debug}; use std::fmt::{self, Formatter}; // use dioxus_core::prelude::*; +use generational_box::SyncStorage; use crate::use_signal; use crate::{dependency::Dependency, CopyValue}; #[derive(Copy, Clone, PartialEq)] pub(crate) struct EffectStack { - pub(crate) effects: CopyValue>, + pub(crate) effects: CopyValue, SyncStorage>, } impl Default for EffectStack { @@ -68,7 +69,7 @@ pub fn use_effect_with_dependencies( #[derive(Copy, Clone, PartialEq)] pub struct Effect { pub(crate) source: ScopeId, - pub(crate) callback: CopyValue>, + pub(crate) callback: CopyValue, SyncStorage>, pub(crate) effect_stack: EffectStack, } diff --git a/packages/signals/src/impls.rs b/packages/signals/src/impls.rs index 9d6ea3019..e97ebc452 100644 --- a/packages/signals/src/impls.rs +++ b/packages/signals/src/impls.rs @@ -1,5 +1,7 @@ use crate::rt::CopyValue; use crate::signal::{ReadOnlySignal, Signal, Write}; +use crate::SignalData; +use generational_box::{MappableMut, Storage}; use parking_lot::{MappedRwLockReadGuard, MappedRwLockWriteGuard}; use std::{ @@ -203,19 +205,19 @@ macro_rules! write_impls { }; } -read_impls!(CopyValue); -write_impls!(CopyValue); -read_impls!(Signal); -write_impls!(Signal); -read_impls!(ReadOnlySignal); +// read_impls!(CopyValue); +// write_impls!(CopyValue); +// read_impls!(Signal); +// write_impls!(Signal); +// read_impls!(ReadOnlySignal); /// An iterator over the values of a `CopyValue>`. -pub struct CopyValueIterator { +pub struct CopyValueIterator> { index: usize, - value: CopyValue>, + value: CopyValue, S>, } -impl Iterator for CopyValueIterator { +impl> Iterator for CopyValueIterator { type Item = T; fn next(&mut self) -> Option { @@ -225,8 +227,8 @@ impl Iterator for CopyValueIterator { } } -impl IntoIterator for CopyValue> { - type IntoIter = CopyValueIterator; +impl> IntoIterator for CopyValue, S> { + type IntoIter = CopyValueIterator; type Item = T; @@ -238,27 +240,33 @@ impl IntoIterator for CopyValue> { } } -impl CopyValue> { +impl>> CopyValue, S> +where + >>::Mut: MappableMut, T>, +{ /// Write to an element in the inner vector. - pub fn get_mut(&self, index: usize) -> Option> { + pub fn get_mut(&self, index: usize) -> Option<, T>>::Mapped> { MappedRwLockWriteGuard::try_map(self.write(), |v| v.get_mut(index)).ok() } } -impl CopyValue> { +impl>> CopyValue, S> +where + >>::Mut: MappableMut, T>, +{ /// Deref the inner value mutably. - pub fn as_mut(&self) -> Option> { - MappedRwLockWriteGuard::try_map(self.write(), |v| v.as_mut()).ok() + pub fn as_mut(&self) -> Option<, T>>::Mapped> { + S::Mut::map(self.try_write(), |v| v.as_mut()).ok() } } /// An iterator over items in a `Signal>`. -pub struct SignalIterator { +pub struct SignalIterator> { index: usize, - value: Signal>, + value: Signal, S>, } -impl Iterator for SignalIterator { +impl> Iterator for SignalIterator { type Item = T; fn next(&mut self) -> Option { @@ -268,8 +276,8 @@ impl Iterator for SignalIterator { } } -impl IntoIterator for Signal> { - type IntoIter = SignalIterator; +impl> IntoIterator for Signal, S> { + type IntoIter = SignalIterator; type Item = T; @@ -281,16 +289,22 @@ impl IntoIterator for Signal> { } } -impl Signal> { +impl>>> Signal, S> +where + S::Mut: MappableMut, T>, +{ /// Returns a reference to an element or `None` if out of bounds. - pub fn get_mut(&self, index: usize) -> Option>> { - Write::filter_map(self.write(), |v| v.get_mut(index)) + pub fn get_mut( + &self, + index: usize, + ) -> Option, T>>::Mapped, S, Vec>> { + S::Mut::map(self.write(), |v| v.get_mut(index)) } } -impl Signal> { +impl>>> Signal, S> { /// Returns a reference to an element or `None` if out of bounds. - pub fn as_mut(&self) -> Option>> { + pub fn as_mut(&self) -> Option>, S::Mut, S, Option>> { Write::filter_map(self.write(), |v| v.as_mut()) } } diff --git a/packages/signals/src/rt.rs b/packages/signals/src/rt.rs index c1fb2994d..3499de21a 100644 --- a/packages/signals/src/rt.rs +++ b/packages/signals/src/rt.rs @@ -5,23 +5,12 @@ use std::rc::Rc; use dioxus_core::prelude::*; use dioxus_core::ScopeId; -use generational_box::{GenerationalBox, Owner, Store}; -use parking_lot::{MappedRwLockReadGuard, MappedRwLockWriteGuard}; +use generational_box::AnyStorage; +use generational_box::Storage; +use generational_box::{GenerationalBox, Owner}; use crate::Effect; -static STORE: once_cell::sync::Lazy = once_cell::sync::Lazy::new(Store::default); - -fn current_store() -> Store { - match consume_context() { - Some(rt) => rt, - None => { - let store = Store::default(); - provide_root_context(store).expect("in a virtual dom") - } - } -} - fn current_owner() -> Rc { match Effect::current() { // If we are inside of an effect, we should use the owner of the effect as the owner of the value. @@ -50,14 +39,22 @@ fn owner_in_scope(scope: ScopeId) -> Rc { } } +#[derive(Debug)] /// CopyValue is a wrapper around a value to make the value mutable and Copy. /// /// It is internally backed by [`generational_box::GenerationalBox`]. -pub struct CopyValue { - pub(crate) value: GenerationalBox, +pub struct CopyValue { + pub(crate) value: GenerationalBox, origin_scope: ScopeId, } +impl Copy for CopyValue {} +impl Clone for CopyValue { + fn clone(&self) -> Self { + *self + } +} + #[cfg(feature = "serde")] impl serde::Serialize for CopyValue where @@ -80,7 +77,7 @@ where } } -impl CopyValue { +impl> CopyValue { /// Create a new CopyValue. The value will be stored in the current component. /// /// Once the component this value is created in is dropped, the value will be dropped. @@ -118,22 +115,22 @@ impl CopyValue { } /// Try to read the value. If the value has been dropped, this will return None. - pub fn try_read(&self) -> Option> { + pub fn try_read(&self) -> Option { self.value.try_read() } /// Read the value. If the value has been dropped, this will panic. - pub fn read(&self) -> MappedRwLockReadGuard<'static, T> { + pub fn read(&self) -> S::Ref { self.value.read() } /// Try to write the value. If the value has been dropped, this will return None. - pub fn try_write(&self) -> Option> { + pub fn try_write(&self) -> Option { self.value.try_write() } /// Write the value. If the value has been dropped, this will panic. - pub fn write(&self) -> MappedRwLockWriteGuard<'static, T> { + pub fn write(&self) -> S::Mut { self.value.write() } @@ -155,21 +152,21 @@ impl CopyValue { } } -impl CopyValue { +impl> CopyValue { /// Get the value. If the value has been dropped, this will panic. pub fn value(&self) -> T { self.read().clone() } } -impl PartialEq for CopyValue { +impl> PartialEq for CopyValue { fn eq(&self, other: &Self) -> bool { self.value.ptr_eq(&other.value) } } -impl Deref for CopyValue { - type Target = dyn Fn() -> MappedRwLockReadGuard<'static, T>; +impl> Deref for CopyValue { + type Target = dyn Fn() -> S::Ref; fn deref(&self) -> &Self::Target { // https://github.com/dtolnay/case-studies/tree/master/callable-types diff --git a/packages/signals/src/selector.rs b/packages/signals/src/selector.rs index 556c8437c..de3f206eb 100644 --- a/packages/signals/src/selector.rs +++ b/packages/signals/src/selector.rs @@ -1,4 +1,5 @@ use dioxus_core::prelude::*; +use generational_box::Storage; use crate::dependency::Dependency; use crate::use_signal; @@ -22,10 +23,10 @@ use crate::{get_effect_stack, signal::SignalData, CopyValue, Effect, ReadOnlySig /// } /// ``` #[must_use = "Consider using `use_effect` to rerun a callback when dependencies change"] -pub fn use_selector( +pub fn use_selector>>( cx: &ScopeState, f: impl FnMut() -> R + 'static, -) -> ReadOnlySignal { +) -> ReadOnlySignal { *cx.use_hook(|| selector(f)) } @@ -46,11 +47,11 @@ pub fn use_selector( /// } /// ``` #[must_use = "Consider using `use_effect` to rerun a callback when dependencies change"] -pub fn use_selector_with_dependencies( +pub fn use_selector_with_dependencies>>( cx: &ScopeState, dependencies: D, mut f: impl FnMut(D::Out) -> R + 'static, -) -> ReadOnlySignal +) -> ReadOnlySignal where D::Out: 'static, { @@ -71,7 +72,9 @@ where /// Creates a new Selector. The selector will be run immediately and whenever any signal it reads changes. /// /// Selectors can be used to efficiently compute derived data from signals. -pub fn selector(mut f: impl FnMut() -> R + 'static) -> ReadOnlySignal { +pub fn selector>>( + mut f: impl FnMut() -> R + 'static, +) -> ReadOnlySignal { let state = Signal:: { inner: CopyValue::invalid(), }; diff --git a/packages/signals/src/signal.rs b/packages/signals/src/signal.rs index 43aa0c904..a12b43648 100644 --- a/packages/signals/src/signal.rs +++ b/packages/signals/src/signal.rs @@ -10,6 +10,7 @@ use dioxus_core::{ prelude::{current_scope_id, has_context, provide_context, schedule_update_any}, ScopeId, ScopeState, }; +use generational_box::{AnyStorage, Mappable, MappableMut, Storage}; use parking_lot::{MappedRwLockReadGuard, MappedRwLockWriteGuard}; use crate::{get_effect_stack, CopyValue, Effect, EffectStack}; @@ -46,7 +47,10 @@ use crate::{get_effect_stack, CopyValue, Effect, EffectStack}; /// } /// ``` #[must_use] -pub fn use_signal(cx: &ScopeState, f: impl FnOnce() -> T) -> Signal { +pub fn use_signal>>( + cx: &ScopeState, + f: impl FnOnce() -> T, +) -> Signal { *cx.use_hook(|| Signal::new(f())) } @@ -119,8 +123,8 @@ pub(crate) struct SignalData { /// } /// } /// ``` -pub struct Signal { - pub(crate) inner: CopyValue>, +pub struct Signal { + pub(crate) inner: CopyValue, S>, } #[cfg(feature = "serde")] @@ -137,7 +141,7 @@ impl<'de, T: serde::Deserialize<'de> + 'static> serde::Deserialize<'de> for Sign } } -impl Signal { +impl>> Signal { /// Creates a new Signal. Signals are a Copy state management solution with automatic dependency tracking. pub fn new(value: T) -> Self { Self { @@ -174,7 +178,10 @@ impl Signal { /// Get the current value of the signal. This will subscribe the current scope to the signal. /// If the signal has been dropped, this will panic. - pub fn read(&self) -> MappedRwLockReadGuard<'static, T> { + pub fn read(&self) -> <>>::Ref as Mappable, T>>::Mapped + where + >>::Ref: Mappable, T>, + { let inner = self.inner.read(); if let Some(effect) = inner.effect_stack.current() { let mut effect_subscribers = inner.effect_subscribers.borrow_mut(); @@ -198,14 +205,19 @@ impl Signal { } } } - MappedRwLockReadGuard::map(inner, |v| &v.value) + S::Ref::map(inner, |v| &v.value) } /// Get a mutable reference to the signal's value. /// If the signal has been dropped, this will panic. - pub fn write(&self) -> Write { + pub fn write( + &self, + ) -> Write>>::Mut as MappableMut, T>>::Mapped, S> + where + >>::Mut: MappableMut, T>, + { let inner = self.inner.write(); - let borrow = MappedRwLockWriteGuard::map(inner, |v| &mut v.value); + let borrow = S::Mut::map(inner, |v| &mut v.value); Write { write: borrow, signal: SignalSubscriberDrop { signal: *self }, @@ -260,7 +272,7 @@ impl Signal { } } -impl Signal { +impl>> Signal { /// Get the current value of the signal. This will subscribe the current scope to the signal. /// If the signal has been dropped, this will panic. pub fn value(&self) -> T { @@ -268,21 +280,21 @@ impl Signal { } } -impl Signal { +impl> Signal { /// Invert the boolean value of the signal. This will trigger an update on all subscribers. pub fn toggle(&self) { self.set(!self.value()); } } -impl PartialEq for Signal { +impl>> PartialEq for Signal { fn eq(&self, other: &Self) -> bool { self.inner == other.inner } } -impl Deref for Signal { - type Target = dyn Fn() -> MappedRwLockReadGuard<'static, T>; +impl>> Deref for Signal { + type Target = dyn Fn() -> S::Ref; fn deref(&self) -> &Self::Target { // https://github.com/dtolnay/case-studies/tree/master/callable-types @@ -314,28 +326,31 @@ impl Deref for Signal { } } -struct SignalSubscriberDrop { - signal: Signal, +struct SignalSubscriberDrop>> { + signal: Signal, } -impl Drop for SignalSubscriberDrop { +impl>> Drop for SignalSubscriberDrop { fn drop(&mut self) { self.signal.update_subscribers(); } } /// A mutable reference to a signal's value. -pub struct Write { - write: MappedRwLockWriteGuard<'static, T>, - signal: SignalSubscriberDrop, +pub struct Write, S: Storage>, I: 'static = T> { + write: B, + signal: SignalSubscriberDrop, } -impl Write { +impl, S: Storage>, I: 'static> Write { /// Map the mutable reference to the signal's value to a new type. - pub fn map(myself: Self, f: impl FnOnce(&mut T) -> &mut O) -> Write { + pub fn map(myself: Self, f: impl FnOnce(&mut T) -> &mut O) -> Write + where + S: MappableMut, + { let Self { write, signal } = myself; Write { - write: MappedRwLockWriteGuard::map(write, f), + write: S::map(write, f), signal, } } @@ -344,14 +359,19 @@ impl Write { pub fn filter_map( myself: Self, f: impl FnOnce(&mut T) -> Option<&mut O>, - ) -> Option> { + ) -> Option> + where + S: MappableMut, + { let Self { write, signal } = myself; let write = MappedRwLockWriteGuard::try_map(write, f).ok(); write.map(|write| Write { write, signal }) } } -impl Deref for Write { +impl, S: Storage>, I: 'static> Deref + for Write +{ type Target = T; fn deref(&self) -> &Self::Target { @@ -359,20 +379,20 @@ impl Deref for Write { } } -impl DerefMut for Write { +impl, S: Storage>, I> DerefMut for Write { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.write } } /// A signal that can only be read from. -pub struct ReadOnlySignal { - inner: Signal, +pub struct ReadOnlySignal>> { + inner: Signal, } -impl ReadOnlySignal { +impl>> ReadOnlySignal { /// Create a new read-only signal. - pub fn new(signal: Signal) -> Self { + pub fn new(signal: Signal) -> Self { Self { inner: signal } } @@ -392,21 +412,21 @@ impl ReadOnlySignal { } } -impl ReadOnlySignal { +impl>> ReadOnlySignal { /// Get the current value of the signal. This will subscribe the current scope to the signal. pub fn value(&self) -> T { self.read().clone() } } -impl PartialEq for ReadOnlySignal { +impl>> PartialEq for ReadOnlySignal { fn eq(&self, other: &Self) -> bool { self.inner == other.inner } } -impl Deref for ReadOnlySignal { - type Target = dyn Fn() -> MappedRwLockReadGuard<'static, T>; +impl>> Deref for ReadOnlySignal { + type Target = dyn Fn() -> S::Ref; fn deref(&self) -> &Self::Target { // https://github.com/dtolnay/case-studies/tree/master/callable-types