Skip to content

Commit

Permalink
Experiment with option memos
Browse files Browse the repository at this point in the history
U cannot get this to work. It's close but I think it requires lifetime bounds on the HRTB lifetimes e.g. `F: for<'a, 'store: 'a> ...`, which currently isn't possible (seems to have been possible at one point? rust-lang/rust#50555 Could also be just that the syntax didn't result in an error, but had no semantic meaning attached to it.)
  • Loading branch information
RSSchermer committed Apr 6, 2022
1 parent b41cb91 commit 91432d9
Show file tree
Hide file tree
Showing 9 changed files with 118 additions and 23 deletions.
2 changes: 2 additions & 0 deletions examples/playground.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![feature(generic_associated_types)]

fn main() {
use futures::StreamExt;

Expand Down
14 changes: 7 additions & 7 deletions src/memo/cell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub struct CellMemo<C, S> {
_marker: marker::PhantomData<*const C>,
}

impl<C, S, T> CellMemo<C, S>
impl<C, S, T: 'static> CellMemo<C, S>
where
C: TypeConstructor,
S: for<'a, 'store> Fn(&'a C::Type<'store>, ReadContext<'store>) -> &'a VersionedCell<'store, T>,
Expand All @@ -29,14 +29,14 @@ where
}
}

impl<C, S, T> Memo for CellMemo<C, S>
impl<C, S, T: 'static> Memo for CellMemo<C, S>
where
C: TypeConstructor,
S: for<'a, 'store> Fn(&'a C::Type<'store>, ReadContext<'store>) -> &'a VersionedCell<'store, T>
+ Clone,
{
type RootTC = C;
type Value<'store> = VersionedCell<'store, T>;
type Value<'a, 'store: 'a> = &'a VersionedCell<'store, T>;
type ValueResolver = CellResolver<C, S>;

fn store_id(&self) -> usize {
Expand All @@ -47,7 +47,7 @@ where
&mut self,
root: &'a C::Type<'store>,
cx: ReadContext<'store>,
) -> Refresh<&'a Self::Value<'store>> {
) -> Refresh<Self::Value<'a, 'store>> {
let cell = (self.select)(root, cx);
let version = cell.version();
let last_version = self.last_version;
Expand All @@ -74,19 +74,19 @@ pub struct CellResolver<C, S> {
_marker: marker::PhantomData<*const C>,
}

impl<C, S, T> ValueResolver for CellResolver<C, S>
impl<C, S, T: 'static> ValueResolver for CellResolver<C, S>
where
C: TypeConstructor,
S: for<'a, 'store> Fn(&'a C::Type<'store>, ReadContext<'store>) -> &'a VersionedCell<'store, T>,
{
type RootTC = C;
type Value<'store> = VersionedCell<'store, T>;
type Value<'a, 'store: 'a> = &'a VersionedCell<'store, T>;

fn select<'a, 'store>(
&self,
root: &'a C::Type<'store>,
cx: ReadContext<'store>,
) -> &'a Self::Value<'store> {
) -> Self::Value<'a, 'store> {
(self.lens)(root, cx)
}
}
11 changes: 4 additions & 7 deletions src/memo/memo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,26 +33,23 @@ impl<T> Deref for Refresh<T> {
pub trait Memo {
type RootTC: TypeConstructor;

type Value<'store>;
type Value<'a, 'store: 'a>;

type ValueResolver: for<'store> ValueResolver<
RootTC = Self::RootTC,
Value<'store> = Self::Value<'store>,
>;
type ValueResolver: ValueResolver;

fn store_id(&self) -> usize;

fn refresh_unchecked<'a, 'store>(
&mut self,
root: &'a <Self::RootTC as TypeConstructor>::Type<'store>,
cx: ReadContext<'store>,
) -> Refresh<&'a Self::Value<'store>>;
) -> Refresh<Self::Value<'a, 'store>>;

fn refresh<'a, 'store>(
&mut self,
root: &'a <Self::RootTC as TypeConstructor>::Type<'store>,
cx: ReadContext<'store>,
) -> Refresh<&'a Self::Value<'store>> {
) -> Refresh<Self::Value<'a, 'store>> {
if self.store_id() != cx.store_id() {
panic!(
"memo is associated with a different store than the read context that was passed"
Expand Down
3 changes: 3 additions & 0 deletions src/memo/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,8 @@ pub use self::memo::*;
mod node;
pub use self::node::*;

mod option_cell;
pub use self::option_cell::*;

mod value_resolver;
pub use self::value_resolver::*;
8 changes: 4 additions & 4 deletions src/memo/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ where
+ Clone,
{
type RootTC = C;
type Value<'store> = VersionedCell<'store, N::Type<'store>>;
type Value<'a, 'store: 'a> = &'a VersionedCell<'store, N::Type<'store>>;
type ValueResolver = NodeSelector<N, C, S>;

fn store_id(&self) -> usize {
Expand All @@ -57,7 +57,7 @@ where
&mut self,
root: &'a C::Type<'store>,
cx: ReadContext<'store>,
) -> Refresh<&'a Self::Value<'store>> {
) -> Refresh<Self::Value<'a, 'store>> {
let cell = (self.select)(root, cx);
let version = cell.version();
let last_version = self.last_version;
Expand Down Expand Up @@ -96,13 +96,13 @@ where
) -> &'a VersionedCell<'store, N::Type<'store>>,
{
type RootTC = C;
type Value<'store> = VersionedCell<'store, N::Type<'store>>;
type Value<'a, 'store: 'a> = &'a VersionedCell<'store, N::Type<'store>>;

fn select<'a, 'store>(
&self,
root: &'a C::Type<'store>,
cx: ReadContext<'store>,
) -> &'a Self::Value<'store> {
) -> Self::Value<'a, 'store> {
(self.lens)(root, cx)
}
}
93 changes: 93 additions & 0 deletions src/memo/option_cell.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
use std::sync::atomic::AtomicU64;
use std::marker;
use crate::TypeConstructor;
use crate::store::{ReadContext, Store};
use crate::versioned_cell::VersionedCell;
use crate::memo::{Memo, Refresh, ValueResolver};
use std::sync::atomic;

pub struct OptionCellMemo<C, S> {
select: S,
store_id: usize,
last_version: Option<u64>,
_marker: marker::PhantomData<*const C>,
}

impl<C, S, T: 'static> OptionCellMemo<C, S>
where
C: TypeConstructor,
S: for<'a, 'store> Fn(&'a C::Type<'store>, ReadContext<'store>) -> Option<&'a VersionedCell<'store, T>>,
{
pub fn new(store: &Store<C>, select: S) -> Self {
let last_version = store.with(|root, cx| select(root, cx).map(|c| c.version()));

OptionCellMemo {
select,
store_id: store.id(),
last_version,
_marker: marker::PhantomData,
}
}
}

impl<C, S, T: 'static> Memo for OptionCellMemo<C, S>
where
C: TypeConstructor,
S: for<'a, 'store> Fn(&'a C::Type<'store>, ReadContext<'store>) -> Option<&'a VersionedCell<'store, T>>
+ Clone,
{
type RootTC = C;
type Value<'a, 'store: 'a> = Option<&'a VersionedCell<'store, T>>;
type ValueResolver = OptionCellResolver<C, S>;

fn store_id(&self) -> usize {
self.store_id
}

fn refresh_unchecked<'a, 'store>(
&mut self,
root: &'a C::Type<'store>,
cx: ReadContext<'store>,
) -> Refresh<Self::Value<'a, 'store>> {
let cell = (self.select)(root, cx);
let version = cell.map(|c| c.version());
let last_version = self.last_version;

self.last_version = version;

if version == last_version {
Refresh::Unchanged(cell)
} else {
Refresh::Changed(cell)
}
}

fn value_resolver(&self) -> Self::ValueResolver {
OptionCellResolver {
lens: self.select.clone(),
_marker: marker::PhantomData,
}
}
}

pub struct OptionCellResolver<C, S> {
lens: S,
_marker: marker::PhantomData<*const C>,
}

impl<C, S, T: 'static> ValueResolver for OptionCellResolver<C, S>
where
C: TypeConstructor,
S: for<'a, 'store> Fn(&'a C::Type<'store>, ReadContext<'store>) -> Option<&'a VersionedCell<'store, T>>,
{
type RootTC = C;
type Value<'a, 'store: 'a> = Option<&'a VersionedCell<'store, T>>;

fn select<'a, 'store>(
&self,
root: &'a C::Type<'store>,
cx: ReadContext<'store>,
) -> Self::Value<'a, 'store> {
(self.lens)(root, cx)
}
}
4 changes: 2 additions & 2 deletions src/memo/value_resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ use crate::TypeConstructor;
pub trait ValueResolver {
type RootTC: TypeConstructor;

type Value<'store>;
type Value<'a, 'store: 'a>;

fn select<'a, 'store>(
&self,
root: &'a <Self::RootTC as TypeConstructor>::Type<'store>,
cx: ReadContext<'store>,
) -> &'a Self::Value<'store>;
) -> Self::Value<'a, 'store>;
}
2 changes: 1 addition & 1 deletion src/versioned_cell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ impl fmt::Display for BorrowMutError {
}
}

pub struct VersionedCell<'store, T: ?Sized> {
pub struct VersionedCell<'store, T: 'store + ?Sized> {
// Note: don't need atomics to track the version or borrow flag, as they can only change inside
// an update scope, which guarantees there are never sync issues.
version: UnsafeCell<u64>,
Expand Down
4 changes: 2 additions & 2 deletions src/watcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ where
{
pub fn with<F, O>(&self, f: F) -> O
where
F: for<'store> FnOnce(&R::Value<'store>, ReadContext<'store>) -> O,
F: for<'a, 'store> FnOnce(R::Value<'a, 'store>, ReadContext<'store>) -> O,
{
self.store
.with(|root, cx| f(self.resolver.select(root, cx), cx))
Expand Down Expand Up @@ -183,7 +183,7 @@ where
{
pub fn with<F, O>(&self, f: F) -> O
where
F: for<'store> FnOnce((&R0::Value<'store>, &R1::Value<'store>), ReadContext<'store>) -> O,
F: for<'a, 'store> FnOnce((R0::Value<'a, 'store>, R1::Value<'a, 'store>), ReadContext<'store>) -> O,
{
self.store.with(|root, cx| {
let resolver_0 = self.resolver_0.select(root, cx);
Expand Down

0 comments on commit 91432d9

Please sign in to comment.