fix/change: do not trigger every parent when writing to a store subfield

This commit is contained in:
Greg Johnston 2024-08-21 19:38:19 -04:00
parent 3d0fdb1ab0
commit e8be9e31ff
5 changed files with 198 additions and 10 deletions

View file

@ -75,6 +75,7 @@ impl<T> StoreField for ArcField<T> {
type Value = T;
type Reader = StoreFieldReader<T>;
type Writer = StoreFieldWriter<T>;
type UntrackedWriter = StoreFieldWriter<T>;
fn get_trigger(&self, path: StorePath) -> ArcTrigger {
(self.get_trigger)(path)
@ -91,6 +92,12 @@ impl<T> StoreField for ArcField<T> {
fn writer(&self) -> Option<Self::Writer> {
(self.write)().map(StoreFieldWriter::new)
}
fn untracked_writer(&self) -> Option<Self::UntrackedWriter> {
let mut writer = (self.write)().map(StoreFieldWriter::new)?;
writer.untrack();
Some(writer)
}
}
impl<Inner, Prev, T> From<Subfield<Inner, Prev, T>> for ArcField<T>

View file

@ -27,6 +27,7 @@ where
type Value = T;
type Reader = StoreFieldReader<T>;
type Writer = StoreFieldWriter<T>;
type UntrackedWriter = StoreFieldWriter<T>;
fn get_trigger(&self, path: StorePath) -> ArcTrigger {
self.inner
@ -49,6 +50,12 @@ where
fn writer(&self) -> Option<Self::Writer> {
self.inner.try_get_value().and_then(|inner| inner.writer())
}
fn untracked_writer(&self) -> Option<Self::UntrackedWriter> {
self.inner
.try_get_value()
.and_then(|inner| inner.untracked_writer())
}
}
impl<Inner, Prev, T, S> From<Subfield<Inner, Prev, T>> for Field<T, S>

View file

@ -77,6 +77,8 @@ where
type Reader = MappedMutArc<Inner::Reader, Prev::Output>;
type Writer =
MappedMutArc<WriteGuard<ArcTrigger, Inner::Writer>, Prev::Output>;
type UntrackedWriter =
MappedMutArc<WriteGuard<ArcTrigger, Inner::Writer>, Prev::Output>;
fn path(&self) -> impl IntoIterator<Item = StorePathSegment> {
self.inner
@ -109,6 +111,12 @@ where
move |n| &mut n[index],
))
}
fn untracked_writer(&self) -> Option<Self::UntrackedWriter> {
let mut guard = self.writer()?;
guard.untrack();
Some(guard)
}
}
impl<Inner, Prev> DefinedAt for AtIndex<Inner, Prev>

View file

@ -2,23 +2,31 @@ use crate::{
path::{StorePath, StorePathSegment},
ArcStore, Store,
};
use guardian::ArcRwLockWriteGuardian;
use or_poisoned::OrPoisoned;
use reactive_graph::{
owner::Storage,
signal::{
guards::{Plain, WriteGuard},
guards::{Mapped, MappedMut, Plain, UntrackedWriteGuard, WriteGuard},
ArcTrigger,
},
traits::{DefinedAt, UntrackableGuard},
traits::{
DefinedAt, IsDisposed, ReadUntracked, Track, Trigger, UntrackableGuard,
Writeable,
},
unwrap_signal,
};
use std::{iter, ops::Deref, sync::Arc};
use std::{
iter,
ops::{Deref, DerefMut},
panic::Location,
sync::Arc,
};
pub trait StoreField: Sized {
type Value;
type Reader: Deref<Target = Self::Value>;
type Writer: UntrackableGuard<Target = Self::Value>;
type UntrackedWriter: DerefMut<Target = Self::Value>;
fn get_trigger(&self, path: StorePath) -> ArcTrigger;
@ -27,6 +35,23 @@ pub trait StoreField: Sized {
fn reader(&self) -> Option<Self::Reader>;
fn writer(&self) -> Option<Self::Writer>;
fn untracked_writer(&self) -> Option<Self::UntrackedWriter>;
#[track_caller]
fn then<T>(
self,
map_fn: fn(&Self::Value) -> &T,
map_fn_mut: fn(&mut Self::Value) -> &mut T,
) -> Then<T, Self> {
Then {
#[cfg(debug_assertions)]
defined_at: Location::caller(),
inner: self,
map_fn,
map_fn_mut,
}
}
}
impl<T> StoreField for ArcStore<T>
@ -35,7 +60,8 @@ where
{
type Value = T;
type Reader = Plain<T>;
type Writer = WriteGuard<ArcTrigger, ArcRwLockWriteGuardian<T>>;
type Writer = WriteGuard<ArcTrigger, UntrackedWriteGuard<T>>;
type UntrackedWriter = UntrackedWriteGuard<T>;
fn get_trigger(&self, path: StorePath) -> ArcTrigger {
let triggers = &self.signals;
@ -53,10 +79,13 @@ where
fn writer(&self) -> Option<Self::Writer> {
let trigger = self.get_trigger(Default::default());
let guard =
ArcRwLockWriteGuardian::take(Arc::clone(&self.value)).ok()?;
let guard = self.untracked_writer()?;
Some(WriteGuard::new(trigger, guard))
}
fn untracked_writer(&self) -> Option<Self::UntrackedWriter> {
UntrackedWriteGuard::try_new(Arc::clone(&self.value))
}
}
impl<T, S> StoreField for Store<T, S>
@ -66,7 +95,8 @@ where
{
type Value = T;
type Reader = Plain<T>;
type Writer = WriteGuard<ArcTrigger, ArcRwLockWriteGuardian<T>>;
type Writer = WriteGuard<ArcTrigger, UntrackedWriteGuard<T>>;
type UntrackedWriter = UntrackedWriteGuard<T>;
fn get_trigger(&self, path: StorePath) -> ArcTrigger {
self.inner
@ -89,4 +119,132 @@ where
fn writer(&self) -> Option<Self::Writer> {
self.inner.try_get_value().and_then(|n| n.writer())
}
fn untracked_writer(&self) -> Option<Self::UntrackedWriter> {
self.inner
.try_get_value()
.and_then(|n| n.untracked_writer())
}
}
#[derive(Debug, Copy, Clone)]
pub struct Then<T, S>
where
S: StoreField,
{
inner: S,
map_fn: fn(&S::Value) -> &T,
map_fn_mut: fn(&mut S::Value) -> &mut T,
#[cfg(debug_assertions)]
defined_at: &'static Location<'static>,
}
impl<T, S> StoreField for Then<T, S>
where
S: StoreField,
{
type Value = T;
type Reader = Mapped<S::Reader, T>;
type Writer = MappedMut<S::Writer, T>;
type UntrackedWriter = MappedMut<S::UntrackedWriter, T>;
fn get_trigger(&self, path: StorePath) -> ArcTrigger {
self.inner.get_trigger(path)
}
fn path(&self) -> impl IntoIterator<Item = StorePathSegment> {
self.inner.path()
}
fn reader(&self) -> Option<Self::Reader> {
let inner = self.inner.reader()?;
Some(Mapped::new_with_guard(inner, self.map_fn))
}
fn writer(&self) -> Option<Self::Writer> {
let inner = self.inner.writer()?;
Some(MappedMut::new(inner, self.map_fn, self.map_fn_mut))
}
fn untracked_writer(&self) -> Option<Self::UntrackedWriter> {
let inner = self.inner.untracked_writer()?;
Some(MappedMut::new(inner, self.map_fn, self.map_fn_mut))
}
}
impl<T, S> DefinedAt for Then<T, S>
where
S: StoreField,
{
fn defined_at(&self) -> Option<&'static Location<'static>> {
#[cfg(debug_assertions)]
{
Some(self.defined_at)
}
#[cfg(not(debug_assertions))]
{
None
}
}
}
impl<T, S> IsDisposed for Then<T, S>
where
S: StoreField + IsDisposed,
{
fn is_disposed(&self) -> bool {
self.inner.is_disposed()
}
}
impl<T, S> Trigger for Then<T, S>
where
S: StoreField,
{
fn trigger(&self) {
let trigger = self.get_trigger(self.path().into_iter().collect());
trigger.trigger();
}
}
impl<T, S> Track for Then<T, S>
where
S: StoreField,
{
fn track(&self) {
let trigger = self.get_trigger(self.path().into_iter().collect());
trigger.track();
}
}
impl<T, S> ReadUntracked for Then<T, S>
where
S: StoreField,
{
type Value = <Self as StoreField>::Reader;
fn try_read_untracked(&self) -> Option<Self::Value> {
self.reader()
}
}
impl<T, S> Writeable for Then<T, S>
where
T: 'static,
S: StoreField,
{
type Value = T;
fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> {
self.writer()
}
fn try_write_untracked(
&self,
) -> Option<impl DerefMut<Target = Self::Value>> {
self.writer().map(|mut writer| {
writer.untrack();
writer
})
}
}

View file

@ -80,7 +80,9 @@ where
{
type Value = T;
type Reader = Mapped<Inner::Reader, T>;
type Writer = MappedMut<WriteGuard<ArcTrigger, Inner::Writer>, T>;
type Writer = MappedMut<WriteGuard<ArcTrigger, Inner::UntrackedWriter>, T>;
type UntrackedWriter =
MappedMut<WriteGuard<ArcTrigger, Inner::UntrackedWriter>, T>;
fn path(&self) -> impl IntoIterator<Item = StorePathSegment> {
self.inner
@ -100,9 +102,15 @@ where
fn writer(&self) -> Option<Self::Writer> {
let trigger = self.get_trigger(self.path().into_iter().collect());
let inner = WriteGuard::new(trigger, self.inner.writer()?);
let inner = WriteGuard::new(trigger, self.inner.untracked_writer()?);
Some(MappedMut::new(inner, self.read, self.write))
}
fn untracked_writer(&self) -> Option<Self::UntrackedWriter> {
let mut guard = self.writer()?;
guard.untrack();
Some(guard)
}
}
impl<Inner, Prev, T> DefinedAt for Subfield<Inner, Prev, T>