Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Interior mutability #50

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ keywords = ["ethereum", "functional"]
categories = ["data-structures", "cryptography::cryptocurrencies", ]

[dependencies]
arc-swap = "1.7.1"
derivative = "2.2.0"
ethereum_hashing = "0.6.0"
ethereum_ssz = "0.5.0"
ethereum_ssz_derive = "0.5.0"
itertools = "0.10.3"
parking_lot = "0.12.1"
parking_lot = { version = "0.12.1", features = ["arc_lock"] }
rayon = "1.5.1"
serde = { version = "1.0.0", features = ["derive"] }
tree_hash = "0.6.0"
Expand Down
84 changes: 62 additions & 22 deletions src/interface.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,28 @@
use crate::level_iter::LevelIter;
use crate::update_map::UpdateMap;
use crate::utils::{updated_length, Length};
use crate::utils::{arb_arc_swap, partial_eq_arc_swap, updated_length, Length};
use crate::{
interface_iter::{InterfaceIter, InterfaceIterCow},
iter::Iter,
Cow, Error, Value,
Cow, Error, Tree, Value, ValueRef,
};
use arbitrary::Arbitrary;
use arc_swap::ArcSwap;
use derivative::Derivative;
use std::collections::BTreeMap;
use std::marker::PhantomData;
use std::sync::Arc;
use tree_hash::Hash256;

pub type GetResult<'a, T> = arc_swap::access::MapGuard<
&'a ArcSwap<Arc<T>>,
(),
fn(&'a Arc<Tree<T>>, usize, usize, usize) -> &'a T,
&'a T,
>;

pub trait ImmList<T: Value> {
fn get(&self, idx: usize) -> Option<&T>;
fn get(&self, idx: usize) -> Option<GetResult<T>>;

fn len(&self) -> Length;

Expand All @@ -29,24 +39,42 @@ pub trait MutList<T: Value>: ImmList<T> {
fn validate_push(current_len: usize) -> Result<(), Error>;
fn replace(&mut self, index: usize, value: T) -> Result<(), Error>;
fn update<U: UpdateMap<T>>(
&mut self,
&self,
updates: U,
hash_updates: Option<BTreeMap<(usize, usize), Hash256>>,
) -> Result<(), Error>;
}

#[derive(Debug, PartialEq, Clone, Arbitrary)]
#[derive(Debug, Derivative, Arbitrary)]
#[derivative(PartialEq)]
pub struct Interface<T, B, U>
where
T: Value,
B: MutList<T>,
U: UpdateMap<T>,
{
pub(crate) backing: B,
pub(crate) updates: U,
#[derivative(PartialEq(compare_with = "partial_eq_arc_swap"))]
#[arbitrary(with = arb_arc_swap)]
pub(crate) updates: ArcSwap<Arc<U>>,
pub(crate) _phantom: PhantomData<T>,
}

impl<T, B, U> Clone for Interface<T, B, U>
where
T: Value,
B: MutList<T> + Clone,
U: UpdateMap<T>,
{
fn clone(&self) -> Self {
Self {
backing: self.backing.clone(),
updates: ArcSwap::new(self.updates.load_full()),
_phantom: PhantomData,
}
}
}

impl<T, B, U> Interface<T, B, U>
where
T: Value,
Expand All @@ -56,54 +84,65 @@ where
pub fn new(backing: B) -> Self {
Self {
backing,
updates: U::default(),
updates: ArcSwap::new(U::default()),
_phantom: PhantomData,
}
}

pub fn get(&self, idx: usize) -> Option<&T> {
self.updates.get(idx).or_else(|| self.backing.get(idx))
pub fn get(&self, idx: usize) -> Option<GetResult<T>> {
panic!()

// let values_in_updates = ArcSwap::map(&self.updates, || )
/*
RwLockReadGuard::try_map(self.updates.read(), |updates| updates.get(idx))
.ok()
.map(ValueRef::Pending)
.or_else(|| self.backing.get(idx).map(ValueRef::Applied))
*/
}

pub fn get_mut(&mut self, idx: usize) -> Option<&mut T> {
self.updates
.get_mut()
.get_mut_with(idx, |idx| self.backing.get(idx).cloned())
}

pub fn get_cow(&mut self, index: usize) -> Option<Cow<T>> {
self.updates
.get_mut()
.get_cow_with(index, |idx| self.backing.get(idx))
}

pub fn push(&mut self, value: T) -> Result<(), Error> {
let index = self.len();
B::validate_push(index)?;
self.updates.insert(index, value);
self.updates.get_mut().insert(index, value);

Ok(())
}

pub fn apply_updates(&mut self) -> Result<(), Error> {
if !self.updates.is_empty() {
let updates = std::mem::take(&mut self.updates);
self.backing.update(updates, None)
pub fn apply_updates(&self) -> Result<(), Error> {
let mut updates = self.updates.write();
if !updates.is_empty() {
self.backing.update(std::mem::take(&mut *updates), None)?;
drop(updates);
Ok(())
} else {
Ok(())
}
}

pub fn has_pending_updates(&self) -> bool {
!self.updates.is_empty()
!self.updates.read().is_empty()
}

pub fn iter(&self) -> InterfaceIter<T, U> {
pub fn iter(&self) -> InterfaceIter<T> {
self.iter_from(0)
}

pub fn iter_from(&self, index: usize) -> InterfaceIter<T, U> {
pub fn iter_from(&self, index: usize) -> InterfaceIter<T> {
InterfaceIter {
tree_iter: self.backing.iter_from(index),
updates: &self.updates,
index,
length: self.len(),
}
Expand All @@ -113,7 +152,7 @@ where
let index = 0;
InterfaceIterCow {
tree_iter: self.backing.iter_from(index),
updates: &mut self.updates,
updates: self.updates.get_mut(),
index,
}
}
Expand All @@ -127,18 +166,19 @@ where
}

pub fn len(&self) -> usize {
updated_length(self.backing.len(), &self.updates).as_usize()
updated_length(self.backing.len(), &*self.updates.read()).as_usize()
}

pub fn is_empty(&self) -> bool {
self.len() == 0
}

pub fn bulk_update(&mut self, updates: U) -> Result<(), Error> {
if !self.updates.is_empty() {
let self_updates = self.updates.get_mut();
if !self_updates.is_empty() {
return Err(Error::BulkUpdateUnclean);
}
self.updates = updates;
*self_updates = updates;
Ok(())
}
}
Expand Down
13 changes: 8 additions & 5 deletions src/interface_iter.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
use crate::iter::Iter;
use crate::{Cow, UpdateMap, Value};
use parking_lot::RwLockWriteGuard;

#[derive(Debug)]
pub struct InterfaceIter<'a, T: Value, U: UpdateMap<T>> {
pub struct InterfaceIter<'a, T: Value> {
pub(crate) tree_iter: Iter<'a, T>,
pub(crate) updates: &'a U,
// FIXME(sproul): remove write guard and flush updates prior to iteration?
// pub(crate) updates: RwLockWriteGuard<'a, U>,
pub(crate) index: usize,
pub(crate) length: usize,
}

impl<'a, T: Value, U: UpdateMap<T>> Iterator for InterfaceIter<'a, T, U> {
impl<'a, T: Value> Iterator for InterfaceIter<'a, T> {
type Item = &'a T;

fn next(&mut self) -> Option<&'a T> {
Expand All @@ -20,7 +22,8 @@ impl<'a, T: Value, U: UpdateMap<T>> Iterator for InterfaceIter<'a, T, U> {
let backing_value = self.tree_iter.next();

// Prioritise the value from the update map.
self.updates.get(index).or(backing_value)
// self.updates.get_mut().get(index).or(backing_value)
backing_value
}

fn size_hint(&self) -> (usize, Option<usize>) {
Expand All @@ -29,7 +32,7 @@ impl<'a, T: Value, U: UpdateMap<T>> Iterator for InterfaceIter<'a, T, U> {
}
}

impl<'a, T: Value, U: UpdateMap<T>> ExactSizeIterator for InterfaceIter<'a, T, U> {}
impl<'a, T: Value> ExactSizeIterator for InterfaceIter<'a, T> {}

#[derive(Debug)]
pub struct InterfaceIterCow<'a, T: Value, U: UpdateMap<T>> {
Expand Down
6 changes: 5 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ pub mod iter;
pub mod leaf;
pub mod level_iter;
pub mod list;
pub mod map_option;
pub mod packed_leaf;
mod repeat;
pub mod serde;
mod tests;
pub mod tree;
pub mod update_map;
pub mod utils;
pub mod value_ref;
pub mod vector;

pub use cow::Cow;
Expand All @@ -26,8 +28,10 @@ pub use leaf::Leaf;
pub use list::List;
pub use packed_leaf::PackedLeaf;
pub use tree::Tree;
pub use triomphe::Arc;
// pub use triomphe::Arc;
pub use std::sync::Arc; // FIXME(sproul)
pub use update_map::UpdateMap;
pub use value_ref::ValueRef;
pub use vector::Vector;

use ssz::{Decode, Encode};
Expand Down
Loading