Skip to content

Commit

Permalink
fix(core): 🐛 StateWriter auto unsubscribe when become read only #532
Browse files Browse the repository at this point in the history
  • Loading branch information
wjian23 committed Feb 22, 2024
1 parent 9597272 commit beef117
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 2 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ Please only add new entries below the [Unreleased](#unreleased---releasedate) he

## [@Unreleased] - @ReleaseDate

### Fixed
- Optimization, StateReader auto unsubscribe if not writer(#532 @wjian23)

## [0.2.0-alpha.3] - 2024-02-20

## [0.2.0-alpha.2] - 2024-02-13
Expand Down
1 change: 1 addition & 0 deletions core/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -722,5 +722,6 @@ mod tests {
let map_reader = a.map_reader(|a| a as &dyn T);
map_reader.read().test();
map_writer.write().test_mut();
AppCtx::run_until_stalled();
}
}
18 changes: 18 additions & 0 deletions core/src/state/splitted_state.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use super::{
MapReader, ModifyScope, Notifier, ReadRef, StateReader, StateWriter, WriteRef, WriterControl,
};
use crate::prelude::AppCtx;
use crate::{
context::BuildCtx,
widget::{Render, RenderBuilder, Widget},
Expand All @@ -24,6 +25,21 @@ pub struct SplittedWriter<O, R, W> {
batched_modify: Sc<Cell<ModifyScope>>,
create_at: Instant,
last_modified: Sc<Cell<Instant>>,
ref_count: Sc<Cell<usize>>,
}

impl<O, R, W> Drop for SplittedWriter<O, R, W> {
fn drop(&mut self) {
if self.ref_count.get() == 1 {
let mut notifier = self.notifier.clone();
// we use an async task to unsubscribe to wait the batched modifies to be
// notified.
AppCtx::spawn_local(async move {
notifier.unsubscribe();
})
.unwrap();
}
}
}

pub struct SplittedReader<S, F> {
Expand Down Expand Up @@ -137,6 +153,7 @@ where
batched_modify: self.batched_modify.clone(),
last_modified: self.last_modified.clone(),
create_at: self.create_at,
ref_count: self.ref_count.clone(),
}
}

Expand Down Expand Up @@ -184,6 +201,7 @@ where
batched_modify: <_>::default(),
last_modified: Sc::new(Cell::new(create_at)),
create_at,
ref_count: Sc::new(Cell::new(1)),
}
}

Expand Down
57 changes: 55 additions & 2 deletions core/src/state/stateful.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ bitflags! {
}
}

impl Notifier {
pub(crate) fn unsubscribe(&mut self) { self.0.clone().unsubscribe(); }
}

struct StatefulInfo {
notifier: Notifier,
/// The counter of the writer may be modified the data.
Expand All @@ -56,7 +60,11 @@ impl<W: 'static> StateReader for Stateful<W> {
fn read(&self) -> ReadRef<W> { ReadRef::new(self.data.borrow()) }

#[inline]
fn clone_reader(&self) -> Self::Reader { Reader(self.clone()) }
fn clone_reader(&self) -> Self::Reader {
let this = self.clone();
this.dec_writer();
Reader(this)
}

#[inline]
fn origin_reader(&self) -> &Self::OriginReader { self }
Expand Down Expand Up @@ -362,7 +370,7 @@ impl<W: std::fmt::Debug> std::fmt::Debug for Stateful<W> {

#[cfg(test)]
mod tests {
use std::cell::RefCell;
use std::{cell::RefCell, rc::Rc};

use super::*;
use crate::{test_helper::*, timer::Timer};
Expand All @@ -380,6 +388,51 @@ mod tests {
assert_eq!(stateful.read().size, Size::new(100., 100.));
}

#[test]
fn unsubscribe_when_not_writer() {
crate::reset_test_env!();
struct Guard {
drop_cnt: Rc<RefCell<i32>>,
}
impl Guard {
fn _use(&self) {}
}
impl Drop for Guard {
fn drop(&mut self) { *self.drop_cnt.borrow_mut() += 1; }
}

fn drop_writer_subscribe<W: StateWriter>(w: W, drop_cnt: Rc<RefCell<i32>>) {
let guard = Guard { drop_cnt: drop_cnt.clone() };
let r = w.clone_reader();
w.modifies().subscribe(move |_| {
guard._use();
r.clone_reader();

Check warning on line 409 in core/src/state/stateful.rs

View check run for this annotation

Codecov / codecov/patch

core/src/state/stateful.rs#L408-L409

Added lines #L408 - L409 were not covered by tests
});
}

let drop_cnt = Rc::new(RefCell::new(0));
{
drop_writer_subscribe(Stateful::new(()), drop_cnt.clone());
};
AppCtx::run_until_stalled();
assert_eq!(*drop_cnt.borrow(), 1);

{
drop_writer_subscribe(Stateful::new(()).map_writer(|v| v, |v| v), drop_cnt.clone());
};
AppCtx::run_until_stalled();
assert_eq!(*drop_cnt.borrow(), 2);

{
drop_writer_subscribe(
Stateful::new(()).split_writer(|v| v, |v| v),
drop_cnt.clone(),
);
};
AppCtx::run_until_stalled();
assert_eq!(*drop_cnt.borrow(), 3);
}

#[test]
fn state_notify_and_relayout() {
crate::reset_test_env!();
Expand Down

0 comments on commit beef117

Please sign in to comment.