Implement len caching for parking_lot RwLock (#10032)

- Refactor (and rename crate) and implement RwLock len cache.

- improve docs

- update ethcore verification queue to new version

- Implement Default, From,  also derive Debug

- update: parking_lot to 0.7
This commit is contained in:
mattrutherford 2018-12-13 18:07:56 +00:00 committed by GitHub
parent 1a2fc03083
commit 60718225ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 313 additions and 36 deletions

6
Cargo.lock generated
View File

@ -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)",
]

View File

@ -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" }

View File

@ -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;

View File

@ -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};

View File

@ -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 <admin@parity.io>"]
[dependencies]
parking_lot = "0.7"

View File

@ -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 <http://www.gnu.org/licenses/>.
//! 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<i32> = 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<T> Len for Vec<T> {
fn len(&self) -> usize { Vec::len(self) }
}
impl<T> Len for VecDeque<T> {
fn len(&self) -> usize { VecDeque::len(self) }
}
impl<T> Len for LinkedList<T> {
fn len(&self) -> usize { LinkedList::len(self) }
}
impl<K: Eq + Hash, V> Len for HashMap<K, V> {
fn len(&self) -> usize { HashMap::len(self) }
}
impl<K, V> Len for BTreeMap<K, V> {
fn len(&self) -> usize { BTreeMap::len(self) }
}
impl<T: Eq + Hash> Len for HashSet<T> {
fn len(&self) -> usize { HashSet::len(self) }
}
impl<T> Len for BTreeSet<T> {
fn len(&self) -> usize { BTreeSet::len(self) }
}
impl<T: Ord> Len for BinaryHeap<T> {
fn len(&self) -> usize { BinaryHeap::len(self) }
}

View File

@ -13,95 +13,107 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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<T> Len for Vec<T> {
fn len(&self) -> usize { self.len() }
}
impl<T> Len for VecDeque<T> {
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<T> {
data: Mutex<T>,
len: AtomicUsize,
#[derive(Debug)]
pub struct LenCachingMutex<T: ?Sized> {
len: AtomicUsize,
data: Mutex<T>,
}
impl<T: Len + Default> Default for LenCachingMutex<T> {
fn default() -> Self {
LenCachingMutex::new(T::default())
}
}
impl<T: Len> From<T> for LenCachingMutex<T> {
fn from(data: T) -> Self {
LenCachingMutex::new(data)
}
}
impl<T: Len> LenCachingMutex<T> {
pub fn new(data: T) -> LenCachingMutex<T> {
/// 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<T: Len + ?Sized> LenCachingMutex<T> {
/// 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<T> {
Guard {
/// Delegates to `parking_lot::Mutex`
/// [`lock()`](../../lock_api/struct.Mutex.html#method.lock).
pub fn lock(&self) -> CachingMutexGuard<T> {
CachingMutexGuard {
mutex_guard: self.data.lock(),
len: &self.len,
}
}
pub fn try_lock(&self) -> Option<Guard<T>> {
Some( Guard {
/// Delegates to `parking_lot::Mutex`
/// [`try_lock()`](../../lock_api/struct.Mutex.html#method.try_lock).
pub fn try_lock(&self) -> Option<CachingMutexGuard<T>> {
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()
}

View File

@ -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 <http://www.gnu.org/licenses/>.
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<T: ?Sized> {
len: AtomicUsize,
data: RwLock<T>,
}
impl<T: Len + Default> Default for LenCachingRwLock<T> {
fn default() -> Self {
LenCachingRwLock::new(T::default())
}
}
impl<T: Len> From<T> for LenCachingRwLock<T> {
fn from(data: T) -> Self {
LenCachingRwLock::new(data)
}
}
impl<T: Len> LenCachingRwLock<T> {
/// Constructs a new LenCachingRwLock
pub fn new(data: T) -> Self {
LenCachingRwLock {
len: AtomicUsize::new(data.len()),
data: RwLock::new(data),
}
}
}
impl<T: Len + ?Sized> LenCachingRwLock<T> {
/// 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<T> {
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<CachingRwLockWriteGuard<T>> {
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<T> {
self.data.read()
}
/// Delegates to `parking_lot::RwLock`
/// [`try_read()`](../../lock_api/struct.RwLock.html#method.try_read).
pub fn try_read(&self) -> Option<RwLockReadGuard<T>> {
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<i32> = Vec::new();
let lcl = LenCachingRwLock::new(v);
assert!(lcl.write().is_empty());
}
#[test]
fn works_with_vecdeque() {
let v: VecDeque<i32> = 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);
}
}