diff --git a/Cargo.lock b/Cargo.lock index 0f764fc8e..14aea010f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -712,7 +712,7 @@ dependencies = [ "kvdb-memorydb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "kvdb-rocksdb 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "len-caching-mutex 0.1.0", + "len-caching-lock 0.1.1", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "lru-cache 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "macros 0.1.0", @@ -1897,8 +1897,8 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "len-caching-mutex" -version = "0.1.0" +name = "len-caching-lock" +version = "0.1.1" dependencies = [ "parking_lot 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index 244359a75..308769cf5 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -70,7 +70,7 @@ kvdb-rocksdb = "0.1.3" serde = "1.0" serde_derive = "1.0" tempdir = {version="0.3", optional = true} -len-caching-mutex = { path = "../util/len-caching-mutex" } +len-caching-lock = { path = "../util/len-caching-lock" } [target.'cfg(any(target_os = "linux", target_os = "macos", target_os = "windows", target_os = "android"))'.dependencies] hardware-wallet = { path = "../hw" } diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index 852cf1e1c..344f1fcc6 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -112,7 +112,7 @@ extern crate journaldb; extern crate serde; #[cfg(any(test, feature = "json-tests", feature = "test-helpers"))] extern crate tempdir; -extern crate len_caching_mutex; +extern crate len_caching_lock; #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows", target_os = "android"))] extern crate hardware_wallet; diff --git a/ethcore/src/verification/queue/mod.rs b/ethcore/src/verification/queue/mod.rs index f6b1f48dc..b1ab7a130 100644 --- a/ethcore/src/verification/queue/mod.rs +++ b/ethcore/src/verification/queue/mod.rs @@ -29,7 +29,7 @@ use io::*; use error::{BlockError, ImportErrorKind, ErrorKind, Error}; use engines::EthEngine; use client::ClientIoMessage; -use len_caching_mutex::LenCachingMutex; +use len_caching_lock::LenCachingMutex; use self::kind::{BlockLike, Kind}; diff --git a/util/len-caching-lock/Cargo.toml b/util/len-caching-lock/Cargo.toml new file mode 100644 index 000000000..41d0e4e70 --- /dev/null +++ b/util/len-caching-lock/Cargo.toml @@ -0,0 +1,10 @@ +[package] +description = "Atomically cached len(), for use with collections contained in parking_lot Mutex and RwLock" +homepage = "http://parity.io" +license = "GPL-3.0" +name = "len-caching-lock" +version = "0.1.1" +authors = ["Parity Technologies "] + +[dependencies] +parking_lot = "0.7" diff --git a/util/len-caching-lock/src/lib.rs b/util/len-caching-lock/src/lib.rs new file mode 100644 index 000000000..553f85ec8 --- /dev/null +++ b/util/len-caching-lock/src/lib.rs @@ -0,0 +1,87 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +//! This crate allows automatic caching of `T.len()` with an api that +//! allows drop in replacement for `parking_lot` +//! [`Mutex`](../lock_api/struct.Mutex.html) +//! and [`RwLock`](../lock_api/struct.RwLock.html) for most common use-cases. +//! +//! This crate implements `Len` for the following types: +//! `std::collections::{VecDeque, LinkedList, HashMap, BTreeMap, HashSet, BTreeSet, BinaryHeap}` +//! +//! ## Example +//! +//! ```rust +//! extern crate len_caching_lock; +//! use len_caching_lock::LenCachingMutex; +//! +//! fn main() { +//! let vec: Vec = Vec::new(); +//! let len_caching_mutex = LenCachingMutex::new(vec); +//! assert_eq!(len_caching_mutex.lock().len(), len_caching_mutex.load_len()); +//! len_caching_mutex.lock().push(0); +//! assert_eq!(1, len_caching_mutex.load_len()); +//! } +//! ``` + +extern crate parking_lot; +use std::collections::{VecDeque, LinkedList, HashMap, BTreeMap, HashSet, BTreeSet, BinaryHeap}; +use std::hash::Hash; + +pub mod mutex; +pub mod rwlock; + +pub use mutex::LenCachingMutex; +pub use rwlock::LenCachingRwLock; + +/// Implement to allow a type with a len() method to be used +/// with [`LenCachingMutex`](mutex/struct.LenCachingMutex.html) +/// or [`LenCachingRwLock`](rwlock/struct.LenCachingRwLock.html) +pub trait Len { + fn len(&self) -> usize; +} + +impl Len for Vec { + fn len(&self) -> usize { Vec::len(self) } +} + +impl Len for VecDeque { + fn len(&self) -> usize { VecDeque::len(self) } +} + +impl Len for LinkedList { + fn len(&self) -> usize { LinkedList::len(self) } +} + +impl Len for HashMap { + fn len(&self) -> usize { HashMap::len(self) } +} + +impl Len for BTreeMap { + fn len(&self) -> usize { BTreeMap::len(self) } +} + +impl Len for HashSet { + fn len(&self) -> usize { HashSet::len(self) } +} + +impl Len for BTreeSet { + fn len(&self) -> usize { BTreeSet::len(self) } +} + +impl Len for BinaryHeap { + fn len(&self) -> usize { BinaryHeap::len(self) } +} diff --git a/util/len-caching-mutex/src/lib.rs b/util/len-caching-lock/src/mutex.rs similarity index 59% rename from util/len-caching-mutex/src/lib.rs rename to util/len-caching-lock/src/mutex.rs index 03a52c56c..b884a2e06 100644 --- a/util/len-caching-mutex/src/lib.rs +++ b/util/len-caching-lock/src/mutex.rs @@ -13,95 +13,107 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -extern crate parking_lot; -use std::collections::VecDeque; use std::ops::{Deref, DerefMut}; use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering; use parking_lot::{Mutex, MutexGuard}; -/// Implement to allow a type with a len() method to be used -/// with [`LenCachingMutex`](struct.LenCachingMutex.html) -pub trait Len { - fn len(&self) -> usize; -} +use Len; -impl Len for Vec { - fn len(&self) -> usize { self.len() } -} - -impl Len for VecDeque { - fn len(&self) -> usize { self.len() } -} - -/// Can be used in place of a `Mutex` where reading `T`'s `len()` without +/// Can be used in place of a [`Mutex`](../../lock_api/struct.Mutex.html) where reading `T`'s `len()` without /// needing to lock, is advantageous. /// When the Guard is released, `T`'s `len()` will be cached. /// The cached `len()` may be at most 1 lock behind current state. -pub struct LenCachingMutex { - data: Mutex, - len: AtomicUsize, +#[derive(Debug)] +pub struct LenCachingMutex { + len: AtomicUsize, + data: Mutex, +} + +impl Default for LenCachingMutex { + fn default() -> Self { + LenCachingMutex::new(T::default()) + } +} + +impl From for LenCachingMutex { + fn from(data: T) -> Self { + LenCachingMutex::new(data) + } } impl LenCachingMutex { - pub fn new(data: T) -> LenCachingMutex { + /// Constructs a new LenCachingMutex + pub fn new(data: T) -> Self { LenCachingMutex { len: AtomicUsize::new(data.len()), data: Mutex::new(data), } } +} - /// Load the most recent value returned from your `T`'s `len()` +impl LenCachingMutex { + /// Load the cached value that was returned from your `T`'s `len()` + /// subsequent to the most recent lock being released. pub fn load_len(&self) -> usize { self.len.load(Ordering::SeqCst) } - pub fn lock(&self) -> Guard { - Guard { + /// Delegates to `parking_lot::Mutex` + /// [`lock()`](../../lock_api/struct.Mutex.html#method.lock). + pub fn lock(&self) -> CachingMutexGuard { + CachingMutexGuard { mutex_guard: self.data.lock(), len: &self.len, } } - pub fn try_lock(&self) -> Option> { - Some( Guard { + /// Delegates to `parking_lot::Mutex` + /// [`try_lock()`](../../lock_api/struct.Mutex.html#method.try_lock). + pub fn try_lock(&self) -> Option> { + Some(CachingMutexGuard { mutex_guard: self.data.try_lock()?, len: &self.len, }) } } -pub struct Guard<'a, T: Len + 'a> { +/// Guard comprising `MutexGuard` and `AtomicUsize` for cache +pub struct CachingMutexGuard<'a, T: Len + 'a + ?Sized> { mutex_guard: MutexGuard<'a, T>, len: &'a AtomicUsize, } -impl<'a, T: Len> Guard<'a, T> { +impl<'a, T: Len + ?Sized> CachingMutexGuard<'a, T> { + /// Returns a mutable reference to the contained + /// [`MutexGuard`](../../parking_lot/mutex/type.MutexGuard.html) pub fn inner_mut(&mut self) -> &mut MutexGuard<'a, T> { &mut self.mutex_guard } + /// Returns a non-mutable reference to the contained + /// [`MutexGuard`](../../parking_lot/mutex/type.MutexGuard.html) pub fn inner(&self) -> &MutexGuard<'a, T> { &self.mutex_guard } } -impl<'a, T: Len> Drop for Guard<'a, T> { +impl<'a, T: Len + ?Sized> Drop for CachingMutexGuard<'a, T> { fn drop(&mut self) { self.len.store(self.mutex_guard.len(), Ordering::SeqCst); } } -impl<'a, T: Len> Deref for Guard<'a, T> { +impl<'a, T: Len + ?Sized> Deref for CachingMutexGuard<'a, T> { type Target = T; fn deref(&self) -> &T { self.mutex_guard.deref() } } -impl<'a, T: Len> DerefMut for Guard<'a, T> { +impl<'a, T: Len + ?Sized> DerefMut for CachingMutexGuard<'a, T> { fn deref_mut(&mut self) -> &mut T { self.mutex_guard.deref_mut() } diff --git a/util/len-caching-lock/src/rwlock.rs b/util/len-caching-lock/src/rwlock.rs new file mode 100644 index 000000000..4d414308e --- /dev/null +++ b/util/len-caching-lock/src/rwlock.rs @@ -0,0 +1,168 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use std::ops::{Deref, DerefMut}; +use std::sync::atomic::AtomicUsize; +use std::sync::atomic::Ordering; + +use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard}; + +use Len; + +/// Can be used in place of a [`RwLock`](../../lock_api/struct.RwLock.html) where +/// reading `T`'s `len()` without needing to lock, is advantageous. +/// When the WriteGuard is released, `T`'s `len()` will be cached. +#[derive(Debug)] +pub struct LenCachingRwLock { + len: AtomicUsize, + data: RwLock, +} + +impl Default for LenCachingRwLock { + fn default() -> Self { + LenCachingRwLock::new(T::default()) + } +} + +impl From for LenCachingRwLock { + fn from(data: T) -> Self { + LenCachingRwLock::new(data) + } +} + +impl LenCachingRwLock { + /// Constructs a new LenCachingRwLock + pub fn new(data: T) -> Self { + LenCachingRwLock { + len: AtomicUsize::new(data.len()), + data: RwLock::new(data), + } + } +} + +impl LenCachingRwLock { + /// Load the cached value that was returned from your `T`'s `len()` + /// subsequent to the most recent lock being released. + pub fn load_len(&self) -> usize { + self.len.load(Ordering::SeqCst) + } + + /// Delegates to `parking_lot::RwLock` + /// [`write()`](../../lock_api/struct.RwLock.html#method.write). + pub fn write(&self) -> CachingRwLockWriteGuard { + CachingRwLockWriteGuard { + write_guard: self.data.write(), + len: &self.len, + } + } + + /// Delegates to `parking_lot::RwLock` + /// [`try_write()`](../../lock_api/struct.RwLock.html#method.try_write). + pub fn try_write(&self) -> Option> { + Some(CachingRwLockWriteGuard { + write_guard: self.data.try_write()?, + len: &self.len, + }) + } + + /// Delegates to `parking_lot::RwLock` + /// [`read()`](../../lock_api/struct.RwLock.html#method.read). + pub fn read(&self) -> RwLockReadGuard { + self.data.read() + } + + /// Delegates to `parking_lot::RwLock` + /// [`try_read()`](../../lock_api/struct.RwLock.html#method.try_read). + pub fn try_read(&self) -> Option> { + self.data.try_read() + } +} + +/// Guard that caches `T`'s `len()` in an `AtomicUsize` when dropped +pub struct CachingRwLockWriteGuard<'a, T: Len + 'a + ?Sized> { + write_guard: RwLockWriteGuard<'a, T>, + len: &'a AtomicUsize, +} + +impl<'a, T: Len + ?Sized> CachingRwLockWriteGuard<'a, T> { + /// Returns a mutable reference to the contained + /// [`RwLockWriteGuard`](../../parking_lot/rwlock/type.RwLockWriteGuard.html) + pub fn inner_mut(&mut self) -> &mut RwLockWriteGuard<'a, T> { + &mut self.write_guard + } + + /// Returns a non-mutable reference to the contained + /// [`RwLockWriteGuard`](../../parking_lot/rwlock/type.RwLockWriteGuard.html) + pub fn inner(&self) -> &RwLockWriteGuard<'a, T> { + &self.write_guard + } +} + +impl<'a, T: Len + ?Sized> Drop for CachingRwLockWriteGuard<'a, T> { + fn drop(&mut self) { + self.len.store(self.write_guard.len(), Ordering::SeqCst); + } +} + +impl<'a, T: Len + ?Sized> Deref for CachingRwLockWriteGuard<'a, T> { + type Target = T; + fn deref(&self) -> &T { + self.write_guard.deref() + } +} + +impl<'a, T: Len + ?Sized> DerefMut for CachingRwLockWriteGuard<'a, T> { + fn deref_mut(&mut self) -> &mut T { + self.write_guard.deref_mut() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::collections::VecDeque; + + #[test] + fn caches_len() { + let v = vec![1,2,3]; + let lcl = LenCachingRwLock::new(v); + assert_eq!(lcl.load_len(), 3); + lcl.write().push(4); + assert_eq!(lcl.load_len(), 4); + } + + #[test] + fn works_with_vec() { + let v: Vec = Vec::new(); + let lcl = LenCachingRwLock::new(v); + assert!(lcl.write().is_empty()); + } + + #[test] + fn works_with_vecdeque() { + let v: VecDeque = VecDeque::new(); + let lcl = LenCachingRwLock::new(v); + lcl.write().push_front(4); + assert_eq!(lcl.load_len(), 1); + } + + #[test] + fn read_works() { + let v = vec![1,2,3]; + let lcl = LenCachingRwLock::new(v); + assert_eq!(lcl.read().len(), 3); + } +}