From 640d9cd498ce22857703b476cdb227f766b9d7cd Mon Sep 17 00:00:00 2001 From: Adoo Date: Fri, 10 Nov 2023 14:41:55 +0800 Subject: [PATCH] =?UTF-8?q?fix(core):=20=F0=9F=90=9B=20invalid=20splitted?= =?UTF-8?q?=20state=20after=20origin=20state=20modified?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/src/render_helper.rs | 12 ++ core/src/state.rs | 269 +++++++++++++++++-------------- core/src/state/map_state.rs | 125 +++++++------- core/src/state/splitted_state.rs | 185 ++++++++++++++------- core/src/state/stateful.rs | 181 ++++++++++----------- core/src/widget.rs | 8 +- 6 files changed, 437 insertions(+), 343 deletions(-) diff --git a/core/src/render_helper.rs b/core/src/render_helper.rs index d49486023..870f62100 100644 --- a/core/src/render_helper.rs +++ b/core/src/render_helper.rs @@ -1,4 +1,6 @@ use crate::prelude::*; +use ribir_algo::Sc; +use std::{cell::RefCell, ops::Deref}; pub(crate) trait RenderTarget { type Target: Render + ?Sized; @@ -50,3 +52,13 @@ impl Render for RenderProxy { impl Query for RenderProxy { crate::widget::impl_proxy_query!(0); } + +impl RenderTarget for RefCell { + type Target = R; + fn proxy(&self, f: impl FnOnce(&Self::Target) -> V) -> V { f(&*self.borrow()) } +} + +impl RenderTarget for Sc { + type Target = R::Target; + fn proxy(&self, f: impl FnOnce(&Self::Target) -> V) -> V { self.deref().proxy(f) } +} diff --git a/core/src/state.rs b/core/src/state.rs index 1ee591e3b..3c98df048 100644 --- a/core/src/state.rs +++ b/core/src/state.rs @@ -3,16 +3,16 @@ mod splitted_state; mod stateful; use std::{ - cell::{Cell, Ref, RefMut, UnsafeCell}, + cell::{Cell, Ref, RefCell, RefMut, UnsafeCell}, convert::Infallible, mem::MaybeUninit, ops::{Deref, DerefMut}, + time::Instant, }; use crate::prelude::*; pub use map_state::*; -use ribir_algo::Sc; -use rxrust::{ops::box_it::BoxOp, subject::Subject}; +use rxrust::ops::box_it::BoxOp; pub use splitted_state::*; pub use stateful::*; @@ -30,10 +30,16 @@ pub trait StateReader: 'static { /// get a clone of this state that only can read. fn clone_reader(&self) -> Self::Reader; /// Maps an reader to another by applying a function to a contained - /// value. The return reader and the origin reader are the same reader. So - /// when one of them is modified, they will both be notified. + /// value. The return reader is just a shortcut to access part of the origin + /// reader. + /// + /// ## Undefined Behavior + /// + /// As `MapReader` is a shortcut to access part of the origin writer, it's + /// assume its part of data is always valid. If the data is invalid, and you + /// use `MapReader` to access it, it's undefined behavior. #[inline] - fn map_reader(&self, map: F) -> MapReader + fn map_reader(&self, map: F) -> MapReader where F: Fn(&Self::Value) -> &U + Clone, { @@ -42,11 +48,22 @@ pub trait StateReader: 'static { /// Return the origin reader that this state map or split from . Otherwise /// return itself. fn origin_reader(&self) -> &Self::OriginReader; + + /// Return the time stamp of the last modifies of this state. + fn time_stamp(&self) -> Instant; + /// Return a modifies `Rx` stream of the state, user can subscribe it to /// response the state changes. - fn modifies(&self) -> BoxOp<'static, ModifyScope, Infallible>; + fn modifies(&self) -> BoxOp<'static, ModifyScope, Infallible> { + self + .raw_modifies() + .filter(|s| s.contains(ModifyScope::DATA)) + .box_it() + } - fn raw_modifies(&self) -> Subject<'static, ModifyScope, Infallible>; + /// Return a modifies `Rx` stream of the state, including all modifies. Use + /// `modifies` instead if you only want to response the data changes. + fn raw_modifies(&self) -> BoxOp<'static, ModifyScope, Infallible>; /// try convert this state into the value, if there is no other share this /// state, otherwise return an error with self. fn try_into_value(self) -> Result @@ -63,53 +80,70 @@ pub trait StateWriter: StateReader { /// Return a silent write reference which notifies will be ignored by the /// framework. fn silent(&self) -> WriteRef; - /// Convert this state reference to a shallow reference. Modify across this - /// reference will notify framework only. That means the modifies on shallow - /// reference should only effect framework but not effect on data. eg. - /// temporary to modify the state and then modifies it back to trigger the - /// view update. Use it only if you know how a shallow reference works. + /// Return a shallow write reference. Modify across this reference will notify + /// framework only. That means the modifies on shallow reference should only + /// effect framework but not effect on data. eg. temporary to modify the + /// state and then modifies it back to trigger the view update. Use it only + /// if you know how a shallow reference works. fn shallow(&self) -> WriteRef; /// Clone this state writer. fn clone_writer(&self) -> Self::Writer; /// Return the origin writer that this state map or split from. fn origin_writer(&self) -> &Self::OriginWriter; - /// Return a new writer by applying a function to the contained value. + /// Return a new writer that be part of the origin writer by applying a + /// function to the contained value. /// /// This writer share the same data with the origin writer. But has it's own /// notifier. When modifies across the return writer, the downstream - /// subscribed on the origin state will not be notified. But when modifies - /// across the origin writer, the downstream subscribed on the return writer - /// will be notified. + /// subscribed on the origin state will not be notified. /// - /// If you want split a new writer that has same notifier with the origin - /// writer, you can use `map_reader(..).into_writer(..)`. + /// If you want a new writer that has same notifier with the origin writer, + /// you can use `map_reader(..).into_writer(..)`. /// - /// #Notice + /// ##Notice /// /// Your `mut_map` function will accept a mutable reference, but you should /// not modify it, just return a mutable reference to a part of it. Because - /// Ribir assume you will not modify the origin state in it. If you modify the - /// origin state in it, no downstream will be notified. + /// Ribir assume you will not modify the origin data in it. If you modify the + /// origin data in it, no downstream will be notified. + /// + /// When you remove/invalid some data across the return writer, you should be + /// careful. Because that part may be in another state reader or writer, if + /// you invalid it, other state reader or writer will be invalid too. + /// + /// ## Panic + /// + /// 1. When the origin writer modifies, the return writer will be invalid, + /// access its value will panic. + /// 2. If modifies the split part data causing any data to be invalid, it may + /// panic. + #[inline] - fn split_writer(&self, map: R, mut_map: W) -> SplittedWriter + fn split_writer(&self, map: R, mut_map: W) -> SplittedWriter where - R: Fn(&Self::Value) -> &V + Clone, - W: Fn(&mut Self::Value) -> &mut V + Clone, + R: Fn(&Self::Value) -> &V + Clone + 'static, + W: Fn(&mut Self::Value) -> &mut V + Clone + 'static, { SplittedWriter::new(self.clone_writer(), map, mut_map) } /// Return a new writer by applying a function to the contained value. The - /// return writer is just a shortcut to access part of the origin writer. They - /// share same data and notifier. - /// #Notice + /// return writer is just a shortcut to access part of the origin writer. + /// + /// ## Notice /// /// Your `mut_map` function will accept a mutable reference, but you should /// not modify it, just return a mutable reference to a part of it. Because - /// Ribir assume you will not modify the origin state in it. If you modify the - /// origin state in it, no downstream will be notified. + /// Ribir assume you will not modify the origin data in it. If you modify the + /// origin data in it, no downstream will be notified. + /// + /// ## Undefined Behavior + /// + /// As `MapWriter` is a shortcut to access part of the origin writer, it's + /// assume its part of data is always valid. If the data is invalid, and you + /// use `MapWriter` to access it, it's undefined behavior. #[inline] - fn map_writer(&self, map: R, mut_map: W) -> MapWriter + fn map_writer(&self, map: R, mut_map: W) -> MapWriter where R: Fn(&Self::Value) -> &V + Clone, W: Fn(&mut Self::Value) -> &mut V + Clone, @@ -125,20 +159,26 @@ pub struct ReadRef<'a, V>(Ref<'a, V>); pub struct WriteRef<'a, V> { value: Option>, - modified: bool, + control: &'a dyn WriterControl, modify_scope: ModifyScope, - batched_modify: Sc>, - notifier: Notifier, + modified: bool, } /// Enum to store both stateless and stateful object. pub struct State(pub(crate) UnsafeCell>); pub(crate) enum InnerState { - Data(StateData), + Data(RefCell), Stateful(Stateful), } +trait WriterControl { + fn last_modified_stamp(&self) -> &Cell; + fn batched_modifies(&self) -> &Cell; + fn notifier(&self) -> &Notifier; + fn dyn_clone(&self) -> Box; +} + impl StateReader for State { type Value = T; type OriginReader = Self; @@ -146,19 +186,22 @@ impl StateReader for State { fn read(&self) -> ReadRef { match self.inner_ref() { - InnerState::Data(w) => w.read(), + InnerState::Data(w) => ReadRef::new(w.borrow()), InnerState::Stateful(w) => w.read(), } } + #[inline] fn clone_reader(&self) -> Self::Reader { self.as_stateful().clone_reader() } #[inline] fn origin_reader(&self) -> &Self::OriginReader { self } - fn modifies(&self) -> BoxOp<'static, ModifyScope, Infallible> { self.as_stateful().modifies() } + #[inline] + fn time_stamp(&self) -> Instant { self.as_stateful().time_stamp() } - fn raw_modifies(&self) -> Subject<'static, ModifyScope, Infallible> { + #[inline] + fn raw_modifies(&self) -> BoxOp<'static, ModifyScope, Infallible> { self.as_stateful().raw_modifies() } @@ -195,18 +238,19 @@ impl State { State(UnsafeCell::new(InnerState::Stateful(stateful))) } - pub fn value(value: W) -> Self { State(UnsafeCell::new(InnerState::Data(StateData::new(value)))) } + pub fn value(value: W) -> Self { State(UnsafeCell::new(InnerState::Data(RefCell::new(value)))) } pub fn as_stateful(&self) -> &Stateful { match self.inner_ref() { InnerState::Data(w) => { - w.assert_is_not_used(); + assert!(w.try_borrow_mut().is_ok()); let mut uninit: MaybeUninit<_> = MaybeUninit::uninit(); // Safety: we already check there is no other reference to the state data. unsafe { std::ptr::copy(w, uninit.as_mut_ptr(), 1); - let stateful = InnerState::Stateful(Stateful::from_state_data(uninit.assume_init())); + let value = uninit.assume_init().into_inner(); + let stateful = InnerState::Stateful(Stateful::new(value)); let copy = std::mem::replace(&mut *self.0.get(), stateful); // this is a copy of the inner data so we need forget it. std::mem::forget(copy); @@ -243,48 +287,6 @@ impl<'a, V> WriteRef<'a, V> { /// is any modifies on this reference. #[inline] pub fn forget_modifies(&mut self) -> bool { std::mem::replace(&mut self.modified, false) } - - pub(crate) fn new( - value: RefMut<'a, V>, - scope: ModifyScope, - batched_modify: Sc>, - notifier: Notifier, - ) -> WriteRef<'a, V> { - WriteRef { - value: Some(value), - modified: false, - modify_scope: scope, - batched_modify, - notifier, - } - } - - pub(crate) fn map(mut orig: WriteRef, f: impl FnOnce(&mut V) -> &mut U) -> WriteRef { - let value = orig.value.take().map(|orig| RefMut::map(orig, f)); - WriteRef { - value, - modified: false, - modify_scope: orig.modify_scope, - batched_modify: orig.batched_modify.clone(), - notifier: orig.notifier.clone(), - } - } - - pub(crate) fn split_map( - mut orig: WriteRef, - f: impl FnOnce(&mut V) -> &mut U, - batched_modify: Sc>, - notifier: Notifier, - ) -> WriteRef { - let value = orig.value.take().map(|orig| RefMut::map(orig, f)); - WriteRef { - value, - modified: false, - modify_scope: orig.modify_scope, - batched_modify, - notifier, - } - } } impl<'a, W> Deref for ReadRef<'a, W> { @@ -310,6 +312,7 @@ impl<'a, W> DerefMut for WriteRef<'a, W> { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { self.modified = true; + self.control.last_modified_stamp().set(Instant::now()); // Safety: value always exists except in drop method. unsafe { self.value.as_mut().unwrap_unchecked().deref_mut() } } @@ -317,24 +320,23 @@ impl<'a, W> DerefMut for WriteRef<'a, W> { impl<'a, W> Drop for WriteRef<'a, W> { fn drop(&mut self) { - if !self.modified { + let Self { control, modify_scope, modified, .. } = self; + if !*modified { return; } - let scope = self.modify_scope; - let batch_scope = self.batched_modify.get(); - if batch_scope.is_empty() && !scope.is_empty() { - self.batched_modify.set(scope); + let batched_modifies = control.batched_modifies(); + if batched_modifies.get().is_empty() && !modify_scope.is_empty() { + batched_modifies.set(*modify_scope); - let mut subject = self.notifier.raw_modifies(); - let share_scope = self.batched_modify.clone(); + let control = control.dyn_clone(); AppCtx::spawn_local(async move { - let scope = share_scope.replace(ModifyScope::empty()); - subject.next(scope); + let scope = control.batched_modifies().replace(ModifyScope::empty()); + control.notifier().next(scope); }) .unwrap(); } else { - self.batched_modify.set(batch_scope | scope); + batched_modifies.set(*modify_scope | batched_modifies.get()); } } } @@ -434,7 +436,7 @@ where macro_rules! impl_compose_builder { ($name: ident) => { - impl ComposeBuilder for $name + impl ComposeBuilder for $name where W: StateWriter, RM: Fn(&W::Value) -> &V + Clone + 'static, @@ -446,7 +448,7 @@ macro_rules! impl_compose_builder { } } - impl ComposeChildBuilder for $name + impl ComposeChildBuilder for $name where W: StateWriter, RM: Fn(&W::Value) -> &V + Clone + 'static, @@ -479,30 +481,23 @@ mod tests { } #[test] - fn path_state_router_test() { + fn map_same_with_origin() { reset_test_env!(); let origin = State::value(Origin { a: 0, b: 0 }); - let a = split_writer!($origin.a); - let b = map_writer!($origin.b); + let map_state = map_writer!($origin.b); let track_origin = Sc::new(Cell::new(0)); - let track_a = Sc::new(Cell::new(0)); - let track_b = Sc::new(Cell::new(0)); + let track_map = Sc::new(Cell::new(0)); let c_origin = track_origin.clone(); origin.modifies().subscribe(move |_| { c_origin.set(c_origin.get() + 1); }); - let c_a = track_a.clone(); - a.modifies().subscribe(move |_| { - c_a.set(c_a.get() + 1); - }); - - let c_b = track_b.clone(); - b.modifies().subscribe(move |_| { - c_b.set(c_b.get() + 1); + let c_map = track_map.clone(); + map_state.modifies().subscribe(move |_| { + c_map.set(c_map.get() + 1); }); origin.write().a = 1; @@ -510,24 +505,62 @@ mod tests { AppCtx::run_until_stalled(); assert_eq!(track_origin.get(), 1); - assert_eq!(track_a.get(), 1); - assert_eq!(track_b.get(), 1); + assert_eq!(track_map.get(), 1); - *a.write() = 1; + *map_state.write() = 1; Timer::wake_timeout_futures(); AppCtx::run_until_stalled(); - assert_eq!(track_origin.get(), 1); - assert_eq!(track_a.get(), 2); - assert_eq!(track_b.get(), 1); + assert_eq!(track_origin.get(), 2); + assert_eq!(track_map.get(), 2); + } + + #[test] + fn split_not_notify_origin() { + reset_test_env!(); + + let origin = State::value(Origin { a: 0, b: 0 }); + let split = split_writer!($origin.b); + + let track_origin = Sc::new(Cell::new(0)); + let track_split = Sc::new(Cell::new(0)); + + let c_origin = track_origin.clone(); + origin.modifies().subscribe(move |_| { + c_origin.set(c_origin.get() + 1); + }); + + let c_split = track_split.clone(); + split.modifies().subscribe(move |_| { + c_split.set(c_split.get() + 1); + }); - *b.write() = 1; + *split.write() = 1; Timer::wake_timeout_futures(); AppCtx::run_until_stalled(); - assert_eq!(track_origin.get(), 2); - assert_eq!(track_a.get(), 3); - assert_eq!(track_b.get(), 2); + assert_eq!(track_origin.get(), 0); + assert_eq!(track_split.get(), 1); + + origin.write().b = 1; + Timer::wake_timeout_futures(); + AppCtx::run_until_stalled(); + assert_eq!(track_origin.get(), 1); + // splitted downstream will not be notified. + assert_eq!(track_split.get(), 1); + } + + #[test] + #[should_panic] + fn invalid_split_after_origin_modify() { + reset_test_env!(); + + let origin = State::value(Origin { a: 0, b: 0 }); + let split = split_writer!($origin.b); + + origin.write().b = 1; + // invalid split state + *split.write() = 1; } struct C; diff --git a/core/src/state/map_state.rs b/core/src/state/map_state.rs index de634fbf4..6384495b1 100644 --- a/core/src/state/map_state.rs +++ b/core/src/state/map_state.rs @@ -5,29 +5,18 @@ use crate::{ }; use super::{ModifyScope, ReadRef, StateReader, StateWriter, WriteRef}; -use rxrust::{ops::box_it::BoxOp, subject::Subject}; -use std::convert::Infallible; +use rxrust::ops::box_it::BoxOp; +use std::{cell::RefMut, convert::Infallible, time::Instant}; /// A state reader that map a reader to another by applying a function on the /// value. This reader is the same reader with the origin reader, It's also have /// the same modifier with the origin state. -// Keep the `V` as the first generic, so the user know the actual value type -// when ide hints. -pub struct MapReader -where - S: StateReader, - F: Fn(&S::Value) -> &V + Clone + 'static, -{ +pub struct MapReader { pub(super) origin: S, pub(super) map: F, } -pub struct MapWriter -where - W: StateWriter, - RM: Fn(&W::Value) -> &V + Clone, - WM: Fn(&mut W::Value) -> &mut V + Clone, -{ +pub struct MapWriter { pub(super) origin: W, pub(super) map: RM, pub(super) mut_map: WM, @@ -35,89 +24,72 @@ where macro_rules! impl_reader_trivial_methods { () => { + type Value = V; + type OriginReader = S; + type Reader = MapReader; + + #[inline] + fn read(&self) -> ReadRef { ReadRef::map(self.origin.read(), &self.map) } + + #[inline] + fn clone_reader(&self) -> Self::Reader { + MapReader { + origin: self.origin.clone_reader(), + map: self.map.clone(), + } + } + #[inline] fn origin_reader(&self) -> &Self::OriginReader { &self.origin } #[inline] - fn modifies(&self) -> BoxOp<'static, ModifyScope, Infallible> { self.origin.modifies() } + fn time_stamp(&self) -> Instant { self.origin.time_stamp() } #[inline] - fn raw_modifies(&self) -> Subject<'static, ModifyScope, Infallible> { - self.origin.raw_modifies() - } + fn raw_modifies(&self) -> BoxOp<'static, ModifyScope, Infallible> { self.origin.raw_modifies() } #[inline] fn try_into_value(self) -> Result { Err(self) } }; } -impl StateReader for MapReader +impl StateReader for MapReader where Self: 'static, - R: StateReader, - F: Fn(&R::Value) -> &V + Clone + 'static, + S: StateReader, + R: Fn(&S::Value) -> &V + Clone + 'static, { - type Value = V; - type OriginReader = R; - type Reader = MapReader; - - #[inline] - fn read(&self) -> ReadRef { ReadRef::map(self.origin.read(), &self.map) } - - #[inline] - fn clone_reader(&self) -> Self::Reader { - MapReader { - origin: self.origin.clone_reader(), - map: self.map.clone(), - } - } - impl_reader_trivial_methods!(); } -impl StateReader for MapWriter +impl StateReader for MapWriter where Self: 'static, - W: StateWriter, - RM: Fn(&W::Value) -> &V + Clone, - WM: Fn(&mut W::Value) -> &mut V + Clone, + S: StateWriter, + R: Fn(&S::Value) -> &V + Clone, + W: Fn(&mut S::Value) -> &mut V + Clone, { - type Value = V; - type OriginReader = W; - type Reader = MapReader; - - #[inline] - fn read(&self) -> ReadRef { ReadRef::map(self.origin.read(), &self.map) } - - #[inline] - fn clone_reader(&self) -> Self::Reader { - MapReader { - origin: self.origin.clone_reader(), - map: self.map.clone(), - } - } - impl_reader_trivial_methods!(); } -impl StateWriter for MapWriter +impl StateWriter for MapWriter where Self: 'static, W: StateWriter, RM: Fn(&W::Value) -> &V + Clone, WM: Fn(&mut W::Value) -> &mut V + Clone, { - type Writer = MapWriter; + type Writer = MapWriter; type OriginWriter = W; #[inline] - fn write(&self) -> WriteRef { WriteRef::map(self.origin.write(), &self.mut_map) } + fn write(&self) -> WriteRef { self.map_ref(self.origin.write()) } #[inline] - fn silent(&self) -> WriteRef { WriteRef::map(self.origin.silent(), &self.mut_map) } + fn silent(&self) -> WriteRef { self.map_ref(self.origin.silent()) } #[inline] - fn shallow(&self) -> WriteRef { WriteRef::map(self.origin.shallow(), &self.mut_map) } + fn shallow(&self) -> WriteRef { self.map_ref(self.origin.shallow()) } #[inline] fn clone_writer(&self) -> Self::Writer { @@ -132,7 +104,7 @@ where fn origin_writer(&self) -> &Self::OriginWriter { &self.origin } } -impl RenderTarget for MapReader +impl RenderTarget for MapReader where S: StateReader, F: Fn(&S::Value) -> &V + Clone + 'static, @@ -146,7 +118,7 @@ where } } -impl RenderBuilder for MapReader +impl RenderBuilder for MapReader where S: StateReader, F: Fn(&S::Value) -> &V + Clone + 'static, @@ -156,7 +128,7 @@ where fn widget_build(self, ctx: &BuildCtx) -> Widget { RenderProxy::new(self).widget_build(ctx) } } -impl RenderBuilder for MapWriter +impl RenderBuilder for MapWriter where Self: 'static, S: StateWriter, @@ -167,3 +139,28 @@ where #[inline] fn widget_build(self, ctx: &BuildCtx) -> Widget { self.clone_reader().widget_build(ctx) } } + +impl MapWriter +where + Self: 'static, + S: StateWriter, + RM: Fn(&S::Value) -> &V + Clone, + WM: Fn(&mut S::Value) -> &mut V + Clone, +{ + fn map_ref<'a>(&'a self, mut orig: WriteRef<'a, S::Value>) -> WriteRef<'a, V> + where + WM: Fn(&mut S::Value) -> &mut V, + { + let value = orig + .value + .take() + .map(|orig| RefMut::map(orig, &self.mut_map)); + + WriteRef { + value, + modified: false, + modify_scope: orig.modify_scope, + control: orig.control, + } + } +} diff --git a/core/src/state/splitted_state.rs b/core/src/state/splitted_state.rs index 5fd2543dd..16c261102 100644 --- a/core/src/state/splitted_state.rs +++ b/core/src/state/splitted_state.rs @@ -1,4 +1,6 @@ -use super::{MapReader, ModifyScope, Notifier, ReadRef, StateReader, StateWriter, WriteRef}; +use super::{ + MapReader, ModifyScope, Notifier, ReadRef, StateReader, StateWriter, WriteRef, WriterControl, +}; use crate::{ context::BuildCtx, widget::{Render, RenderBuilder, Widget}, @@ -6,73 +8,106 @@ use crate::{ use ribir_algo::Sc; use rxrust::{ ops::box_it::BoxOp, - prelude::{ObservableItem, Observer}, - subject::Subject, - subscription::Subscription, + prelude::{BoxIt, ObservableExt}, +}; +use std::{ + cell::{Cell, RefMut}, + time::Instant, }; -use std::{any::Any, cell::Cell}; /// A writer splitted writer from another writer, and has its own notifier. -pub struct SplittedWriter -where - O: StateWriter, - R: Fn(&O::Value) -> &V + Clone, - W: Fn(&mut O::Value) -> &mut V + Clone, -{ +pub struct SplittedWriter { origin: O, map: R, mut_map: W, notifier: Notifier, batched_modify: Sc>, - connect_guard: Sc>, + create_at: Instant, + last_modified: Sc>, } -impl StateReader for SplittedWriter -where - Self: 'static, - O: StateWriter, - R: Fn(&O::Value) -> &V + Clone, - W: Fn(&mut O::Value) -> &mut V + Clone, -{ - type Value = V; - type OriginReader = O; - type Reader = MapReader; +pub struct SplittedReader { + origin: S, + map: F, + notifier: Notifier, + create_at: Instant, + last_modified: Sc>, +} - fn read(&self) -> ReadRef { ReadRef::map(self.origin.read(), &self.map) } +macro_rules! splitted_reader_impl { + () => { + type Value = V; + type OriginReader = O; + type Reader = SplittedReader; + + fn read(&self) -> ReadRef { + assert!( + self.create_at > self.origin.time_stamp(), + "A splitted reader is invalid because its origin state is modified after it created." + ); + ReadRef::map(self.origin.read(), &self.map) + } - #[inline] - fn clone_reader(&self) -> Self::Reader { - MapReader { - origin: self.origin.clone_reader(), - map: self.map.clone(), + #[inline] + fn clone_reader(&self) -> Self::Reader { + SplittedReader { + origin: self.origin.clone_reader(), + map: self.map.clone(), + notifier: self.notifier.clone(), + create_at: self.create_at, + last_modified: self.last_modified.clone(), + } } - } - #[inline] - fn origin_reader(&self) -> &Self::OriginReader { &self.origin } + #[inline] + fn origin_reader(&self) -> &Self::OriginReader { &self.origin } + + #[inline] + fn time_stamp(&self) -> Instant { self.last_modified.get() } + + #[inline] + fn raw_modifies(&self) -> BoxOp<'static, ModifyScope, std::convert::Infallible> { + let origin = self.origin.clone_reader(); + let create_at = self.create_at; + self + .notifier + .raw_modifies() + .take_while(move |_| origin.time_stamp() < create_at) + .box_it() + } - #[inline] - fn modifies(&self) -> BoxOp<'static, ModifyScope, std::convert::Infallible> { - self.notifier.modifies() - } + #[inline] + fn try_into_value(self) -> Result { Err(self) } + }; +} - #[inline] - fn raw_modifies(&self) -> Subject<'static, ModifyScope, std::convert::Infallible> { - self.notifier.raw_modifies() - } +impl StateReader for SplittedReader +where + Self: 'static, + O: StateReader, + R: Fn(&O::Value) -> &V + Clone, +{ + splitted_reader_impl!(); +} - #[inline] - fn try_into_value(self) -> Result { Err(self) } +impl StateReader for SplittedWriter +where + Self: 'static, + O: StateWriter, + R: Fn(&O::Value) -> &V + Clone, + W: Fn(&mut O::Value) -> &mut V + Clone, +{ + splitted_reader_impl!(); } -impl StateWriter for SplittedWriter +impl StateWriter for SplittedWriter where Self: 'static, O: StateWriter, R: Fn(&O::Value) -> &V + Clone, W: Fn(&mut O::Value) -> &mut V + Clone, { - type Writer = SplittedWriter; + type Writer = SplittedWriter; type OriginWriter = O; #[inline] @@ -91,7 +126,8 @@ where mut_map: self.mut_map.clone(), notifier: self.notifier.clone(), batched_modify: self.batched_modify.clone(), - connect_guard: self.connect_guard.clone(), + last_modified: self.last_modified.clone(), + create_at: self.create_at, } } @@ -99,42 +135,67 @@ where fn origin_writer(&self) -> &Self::OriginWriter { &self.origin } } -impl SplittedWriter +impl WriterControl for SplittedWriter where + Self: 'static, O: StateWriter, R: Fn(&O::Value) -> &V + Clone, W: Fn(&mut O::Value) -> &mut V + Clone, { - pub(super) fn new(origin: O, map: R, mut_map: W) -> Self { - let notifier = Notifier::default(); - let c_modifier = notifier.clone(); + #[inline] + fn last_modified_stamp(&self) -> &Cell { &self.last_modified } - let h = origin - .raw_modifies() - .subscribe(move |v| c_modifier.raw_modifies().next(v)) - .unsubscribe_when_dropped(); + #[inline] + fn batched_modifies(&self) -> &Cell { &self.batched_modify } + + #[inline] + fn notifier(&self) -> &Notifier { &self.notifier } + + #[inline] + fn dyn_clone(&self) -> Box { Box::new(self.clone_writer()) } +} + +impl SplittedWriter +where + Self: 'static, + O: StateWriter, + R: Fn(&O::Value) -> &V + Clone, + W: Fn(&mut O::Value) -> &mut V + Clone, +{ + pub(super) fn new(origin: O, map: R, mut_map: W) -> Self { + let create_at = Instant::now(); Self { origin, map, mut_map, - notifier, + notifier: Notifier::default(), batched_modify: <_>::default(), - connect_guard: Sc::new(Box::new(h)), + last_modified: Sc::new(Cell::new(create_at)), + create_at, } } - fn split_ref<'a>(&'a self, origin_ref: WriteRef<'a, O::Value>) -> WriteRef<'a, V> { - WriteRef::split_map( - origin_ref, - &self.mut_map, - self.batched_modify.clone(), - self.notifier.clone(), - ) + fn split_ref<'a>(&'a self, mut orig: WriteRef<'a, O::Value>) -> WriteRef<'a, V> { + assert!( + self.create_at > self.origin.time_stamp(), + "A splitted writer is invalid because its origin state is modified after it created." + ); + let value = orig + .value + .take() + .map(|orig| RefMut::map(orig, &self.mut_map)); + + WriteRef { + value, + modified: false, + modify_scope: orig.modify_scope, + control: self, + } } } -impl RenderBuilder for SplittedWriter +impl RenderBuilder for SplittedWriter where O: StateWriter, R: Fn(&O::Value) -> &V + Clone + 'static, diff --git a/core/src/state/stateful.rs b/core/src/state/stateful.rs index 16b481985..62ad73338 100644 --- a/core/src/state/stateful.rs +++ b/core/src/state/stateful.rs @@ -1,18 +1,18 @@ -use crate::{ - prelude::*, - render_helper::{RenderProxy, RenderTarget}, -}; +use crate::{prelude::*, render_helper::RenderProxy}; use ribir_algo::Sc; use rxrust::{ops::box_it::BoxOp, prelude::*}; use std::{ cell::{Cell, RefCell}, convert::Infallible, + time::Instant, }; +use super::WriterControl; + /// Stateful object use to watch the modifies of the inner data. pub struct Stateful { - pub(crate) inner: Sc>, - pub(crate) notifier: Notifier, + data: Sc>, + info: Sc, } pub struct Reader(Stateful); @@ -36,35 +36,46 @@ bitflags! { } } +struct StatefulInfo { + notifier: Notifier, + /// The counter of the writer may be modified the data. + writer_count: Cell, + /// The batched modifies of the `State` which will be notified. + batch_modified: Cell, + /// The timestamp of the last modify of the data, not record the modify from + /// the splitted or mapped state. + last_modified: Cell, +} + impl StateReader for Stateful { type Value = W; type OriginReader = Self; type Reader = Reader; #[inline] - fn read(&self) -> ReadRef { self.inner.read() } + fn read(&self) -> ReadRef { ReadRef::new(self.data.borrow()) } #[inline] - fn clone_reader(&self) -> Self::Reader { Reader::from_stateful(self) } + fn clone_reader(&self) -> Self::Reader { Reader(self.clone()) } #[inline] fn origin_reader(&self) -> &Self::OriginReader { self } #[inline] - fn modifies(&self) -> BoxOp<'static, ModifyScope, Infallible> { self.notifier.modifies() } + fn time_stamp(&self) -> Instant { self.info.last_modified.get() } #[inline] - fn raw_modifies(&self) -> Subject<'static, ModifyScope, Infallible> { - self.notifier.raw_modifies() + fn raw_modifies(&self) -> BoxOp<'static, ModifyScope, Infallible> { + self.info.notifier.raw_modifies() } fn try_into_value(self) -> Result { - if Sc::ref_count(&self.inner) == 1 { - let inner = self.inner.clone(); + if self.data.ref_count() == 1 { + let data = self.data.clone(); drop(self); // SAFETY: `Rc::strong_count(&self.inner) == 1` guarantees unique access. - let inner = unsafe { Sc::try_unwrap(inner).unwrap_unchecked() }; - Ok(inner.data.into_inner()) + let data = unsafe { Sc::try_unwrap(data).unwrap_unchecked() }; + Ok(data.into_inner()) } else { Err(self) } @@ -75,14 +86,19 @@ impl StateWriter for Stateful { type Writer = Writer; type OriginWriter = Self; + #[inline] fn write(&self) -> WriteRef { self.write_ref(ModifyScope::BOTH) } + #[inline] fn silent(&self) -> WriteRef { self.write_ref(ModifyScope::DATA) } + #[inline] fn shallow(&self) -> WriteRef { self.write_ref(ModifyScope::FRAMEWORK) } - fn clone_writer(&self) -> Self::Writer { Writer::from_stateful(self) } + #[inline] + fn clone_writer(&self) -> Self::Writer { Writer(self.clone()) } + #[inline] fn origin_writer(&self) -> &Self::OriginWriter { self } } @@ -101,10 +117,10 @@ impl StateReader for Reader { fn origin_reader(&self) -> &Self::OriginReader { self } #[inline] - fn modifies(&self) -> BoxOp<'static, ModifyScope, Infallible> { self.0.modifies() } + fn time_stamp(&self) -> Instant { self.0.time_stamp() } #[inline] - fn raw_modifies(&self) -> Subject<'static, ModifyScope, Infallible> { self.0.raw_modifies() } + fn raw_modifies(&self) -> BoxOp<'static, ModifyScope, Infallible> { self.0.raw_modifies() } fn try_into_value(self) -> Result { let inner = self.0.clone_writer().0; @@ -128,10 +144,10 @@ impl StateReader for Writer { fn origin_reader(&self) -> &Self::OriginReader { self } #[inline] - fn modifies(&self) -> BoxOp<'static, ModifyScope, Infallible> { self.0.modifies() } + fn time_stamp(&self) -> Instant { self.0.time_stamp() } #[inline] - fn raw_modifies(&self) -> Subject<'static, ModifyScope, Infallible> { self.0.raw_modifies() } + fn raw_modifies(&self) -> BoxOp<'static, ModifyScope, Infallible> { self.0.raw_modifies() } #[inline] fn try_into_value(self) -> Result { self.0.try_into_value().map_err(Writer) } @@ -157,6 +173,20 @@ impl StateWriter for Writer { fn origin_writer(&self) -> &Self::OriginWriter { self } } +impl WriterControl for Sc { + #[inline] + fn last_modified_stamp(&self) -> &Cell { &self.last_modified } + + #[inline] + fn batched_modifies(&self) -> &Cell { &self.batch_modified } + + #[inline] + fn notifier(&self) -> &Notifier { &self.notifier } + + #[inline] + fn dyn_clone(&self) -> Box { Box::new(self.clone()) } +} + impl Drop for Reader { fn drop(&mut self) { // The `Stateful` is a writer but used as a reader in `Reader` that not @@ -171,7 +201,7 @@ impl Drop for Stateful { self.dec_writer(); // can cancel the notifier, because no one will modify the data. if self.writer_count() == 0 { - let notifier = self.notifier.clone(); + let notifier = self.info.notifier.clone(); // we use an async task to unsubscribe to wait the batched modifies to be // notified. AppCtx::spawn_local(async move { @@ -183,40 +213,15 @@ impl Drop for Stateful { // Declare object may add task to disconnect to upstream, trigger that task if // this is the last reference. We not hold that task in `Stateful` to avoid // cycle reference. - if self.inner.ref_count() == 1 { + if self.data.ref_count() == 1 { AppCtx::trigger_task(self.heap_ptr() as *const ()); } } } -impl Reader { - fn from_stateful(stateful: &Stateful) -> Self { - Reader(Stateful { - inner: stateful.inner.clone(), - notifier: stateful.notifier.clone(), - }) - } -} - impl Writer { #[inline] pub fn into_inner(self) -> Stateful { self.0 } - - fn from_stateful(stateful: &Stateful) -> Self { - stateful.inc_writer(); - Writer(Stateful { - inner: stateful.inner.clone(), - notifier: stateful.notifier.clone(), - }) - } -} - -pub(crate) struct StateData { - data: RefCell, - /// The batched modifies of the `State` which will be notified. - batch_modified: Sc>, - /// The counter of the writer may be modified the data. - writer_count: Cell, } macro_rules! compose_builder_impl { @@ -243,7 +248,7 @@ impl RenderBuilder for Stateful { match self.try_into_value() { Ok(r) => r.widget_build(ctx), Err(s) => { - let w = RenderProxy::new(s.inner.clone()).widget_build(ctx); + let w = RenderProxy::new(s.data.clone()).widget_build(ctx); w.dirty_subscribe(s.raw_modifies(), ctx) } } @@ -262,9 +267,9 @@ impl RenderBuilder for Reader { impl Stateful { pub fn new(data: W) -> Self { - Stateful { - inner: Sc::new(StateData::new(data)), - notifier: <_>::default(), + Self { + data: Sc::new(RefCell::new(data)), + info: Sc::new(StatefulInfo::new()), } } @@ -282,46 +287,40 @@ impl Stateful { /// return the heap pointer of the data. #[inline] - fn heap_ptr(&self) -> *const W { self.inner.data.as_ptr() } + fn heap_ptr(&self) -> *const W { self.data.as_ptr() } - pub(crate) fn from_state_data(data: StateData) -> Self { - Self { - inner: Sc::new(data), - notifier: <_>::default(), + fn write_ref(&self, scope: ModifyScope) -> WriteRef<'_, W> { + let value = self.data.borrow_mut(); + WriteRef { + value: Some(value), + modified: false, + modify_scope: scope, + control: &self.info, } } - fn write_ref(&self, scope: ModifyScope) -> WriteRef<'_, W> { - let value = self.inner.data.borrow_mut(); - let batched_modify = self.inner.batch_modified.clone(); - let modifier = self.notifier.clone(); - WriteRef::new(value, scope, batched_modify, modifier) - } + fn writer_count(&self) -> usize { self.info.writer_count.get() } + fn inc_writer(&self) { self.info.writer_count.set(self.writer_count() + 1); } + fn dec_writer(&self) { self.info.writer_count.set(self.writer_count() - 1); } - fn writer_count(&self) -> usize { self.inner.writer_count.get() } - fn inc_writer(&self) { self.inner.writer_count.set(self.writer_count() + 1); } - fn dec_writer(&self) { self.inner.writer_count.set(self.writer_count() - 1); } + fn clone(&self) -> Self { + self.inc_writer(); + Self { + data: self.data.clone(), + info: self.info.clone(), + } + } } -impl StateData { - /// Assert the state data is not used by any reader and writer. - #[inline] - #[track_caller] - pub(crate) fn assert_is_not_used(&self) { self.data.borrow_mut(); } - - #[inline] - pub(crate) fn new(data: W) -> Self { - Self { - // the `StateData` in `Stateful` or `State` is a writer - writer_count: Cell::new(1), - data: RefCell::new(data), +impl StatefulInfo { + fn new() -> Self { + StatefulInfo { batch_modified: <_>::default(), + writer_count: Cell::new(1), + notifier: <_>::default(), + last_modified: Cell::new(Instant::now()), } } - - pub(crate) fn into_inner(self) -> W { self.data.into_inner() } - - pub(crate) fn read(&self) -> ReadRef { ReadRef::new(self.data.borrow()) } } impl SingleChild for Stateful {} @@ -345,30 +344,18 @@ impl MultiParent for Stateful { } } -impl RenderTarget for Sc> { - type Target = R; - fn proxy(&self, f: impl FnOnce(&Self::Target) -> V) -> V { f(&*self.read()) } -} - -impl Query for StateData { - crate::widget::impl_proxy_query!(read()); -} - impl Notifier { - pub fn modifies(&self) -> BoxOp<'static, ModifyScope, Infallible> { - self - .raw_modifies() - .filter(|s| s.contains(ModifyScope::DATA)) - .box_it() + pub(crate) fn raw_modifies(&self) -> BoxOp<'static, ModifyScope, Infallible> { + self.0.clone().box_it() } - pub(crate) fn raw_modifies(&self) -> Subject<'static, ModifyScope, Infallible> { self.0.clone() } + pub(crate) fn next(&self, scope: ModifyScope) { self.0.clone().next(scope) } } impl std::fmt::Debug for Stateful { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_tuple("Stateful") - .field(&*self.inner.read()) + .field(&*self.data.borrow()) .finish() } } diff --git a/core/src/widget.rs b/core/src/widget.rs index e4739fded..8ad67d7b3 100644 --- a/core/src/widget.rs +++ b/core/src/widget.rs @@ -4,14 +4,15 @@ use crate::{ prelude::*, }; use ribir_algo::{Sc, ShareResource}; +use rxrust::ops::box_it::BoxOp; -use std::convert::Infallible; #[doc(hidden)] pub use std::{ any::{Any, TypeId}, marker::PhantomData, ops::Deref, }; +use std::{cell::RefCell, convert::Infallible}; pub trait Compose: Sized { /// Describes the part of the user interface represented by this widget. /// Called by framework, should never directly call it. @@ -182,7 +183,7 @@ impl Widget { /// `upstream` emit a modify event that contains `ModifyScope::FRAMEWORK`. pub(crate) fn dirty_subscribe( self, - upstream: Subject<'static, ModifyScope, Infallible>, + upstream: BoxOp<'static, ModifyScope, Infallible>, ctx: &BuildCtx, ) -> Self { let dirty_set = ctx.tree.borrow().dirty_set.clone(); @@ -329,6 +330,9 @@ impl Query for ShareResource { impl Query for Sc { impl_proxy_query!(deref()); } +impl Query for RefCell { + impl_proxy_query!(borrow()); +} impl_proxy_render!(proxy deref(), ShareResource, , where T: Render + 'static); pub(crate) fn hit_test_impl(ctx: &HitTestCtx, pos: Point) -> bool {