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:
parent
1a2fc03083
commit
60718225ac
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -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)",
|
||||
]
|
||||
|
@ -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" }
|
||||
|
@ -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;
|
||||
|
@ -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};
|
||||
|
||||
|
10
util/len-caching-lock/Cargo.toml
Normal file
10
util/len-caching-lock/Cargo.toml
Normal 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"
|
87
util/len-caching-lock/src/lib.rs
Normal file
87
util/len-caching-lock/src/lib.rs
Normal 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) }
|
||||
}
|
@ -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()
|
||||
}
|
168
util/len-caching-lock/src/rwlock.rs
Normal file
168
util/len-caching-lock/src/rwlock.rs
Normal 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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user