Skip to main content

len_caching_lock/
rwlock.rs

1// Copyright 2015-2020 Parity Technologies (UK) Ltd.
2// This file is part of Tetsy Vapory.
3
4// Tetsy Vapory is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Tetsy Vapory is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Tetsy Vapory.  If not, see <http://www.gnu.org/licenses/>.
16
17use std::ops::{Deref, DerefMut};
18use std::sync::atomic::AtomicUsize;
19use std::sync::atomic::Ordering;
20
21use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard};
22
23use Len;
24
25/// Can be used in place of a [`RwLock`](../../lock_api/struct.RwLock.html) where 
26/// reading `T`'s `len()` without needing to lock, is advantageous. 
27/// When the WriteGuard is released, `T`'s `len()` will be cached.
28#[derive(Debug)]
29pub struct LenCachingRwLock<T: ?Sized> {
30	len: AtomicUsize,
31	data: RwLock<T>,
32}
33
34impl<T: Len + Default> Default for LenCachingRwLock<T> {
35	fn default() -> Self {
36		LenCachingRwLock::new(T::default())
37	}
38}
39
40impl<T: Len> From<T> for LenCachingRwLock<T> {
41	fn from(data: T) -> Self {
42		LenCachingRwLock::new(data)
43	}
44}
45
46impl<T: Len> LenCachingRwLock<T> {
47	/// Constructs a new LenCachingRwLock
48	pub fn new(data: T) -> Self {
49		LenCachingRwLock {
50			len: AtomicUsize::new(data.len()),
51			data: RwLock::new(data),
52		}
53	}
54}
55
56impl<T: Len + ?Sized> LenCachingRwLock<T> {
57	/// Load the cached value that was returned from your `T`'s `len()`
58	/// subsequent to the most recent lock being released.
59	pub fn load_len(&self) -> usize {
60		self.len.load(Ordering::SeqCst)
61	}
62
63	/// Delegates to `parking_lot::RwLock`
64	/// [`write()`](../../lock_api/struct.RwLock.html#method.write).
65	pub fn write(&self) -> CachingRwLockWriteGuard<T> {
66		CachingRwLockWriteGuard {
67			write_guard: self.data.write(),
68			len: &self.len,
69		}
70	}
71
72	/// Delegates to `parking_lot::RwLock`
73	/// [`try_write()`](../../lock_api/struct.RwLock.html#method.try_write).
74	pub fn try_write(&self) -> Option<CachingRwLockWriteGuard<T>> {
75		Some(CachingRwLockWriteGuard {
76			write_guard: self.data.try_write()?,
77			len: &self.len,
78		})
79	}
80
81	/// Delegates to `parking_lot::RwLock`
82	/// [`read()`](../../lock_api/struct.RwLock.html#method.read).
83	pub fn read(&self) -> RwLockReadGuard<T> {
84		self.data.read()
85	}
86
87	/// Delegates to `parking_lot::RwLock`
88	/// [`try_read()`](../../lock_api/struct.RwLock.html#method.try_read).
89	pub fn try_read(&self) -> Option<RwLockReadGuard<T>> {
90		self.data.try_read()
91	}
92}
93
94/// Guard that caches `T`'s `len()` in an `AtomicUsize` when dropped
95pub struct CachingRwLockWriteGuard<'a, T: Len + 'a + ?Sized> {
96	write_guard: RwLockWriteGuard<'a, T>,
97	len: &'a AtomicUsize,
98}
99
100impl<'a, T: Len + ?Sized> CachingRwLockWriteGuard<'a, T> {
101	/// Returns a mutable reference to the contained
102	/// [`RwLockWriteGuard`](../../parking_lot/rwlock/type.RwLockWriteGuard.html)
103	pub fn inner_mut(&mut self) -> &mut RwLockWriteGuard<'a, T> {
104		&mut self.write_guard
105	}
106
107	/// Returns a non-mutable reference to the contained
108	/// [`RwLockWriteGuard`](../../parking_lot/rwlock/type.RwLockWriteGuard.html)
109	pub fn inner(&self) -> &RwLockWriteGuard<'a, T> {
110		&self.write_guard
111	}
112}
113
114impl<'a, T: Len + ?Sized> Drop for CachingRwLockWriteGuard<'a, T> {
115	fn drop(&mut self) {
116		self.len.store(self.write_guard.len(), Ordering::SeqCst);
117	}
118}
119
120impl<'a, T: Len + ?Sized> Deref for CachingRwLockWriteGuard<'a, T> {
121	type Target = T;
122	fn deref(&self)	-> &T {
123		self.write_guard.deref()
124	}
125}
126
127impl<'a, T: Len + ?Sized> DerefMut for CachingRwLockWriteGuard<'a, T> {
128	fn deref_mut(&mut self)	-> &mut T {
129		self.write_guard.deref_mut()
130	}
131}
132
133#[cfg(test)]
134mod tests {
135	use super::*;
136	use std::collections::VecDeque;
137
138	#[test]
139	fn caches_len() {
140		let v = vec![1,2,3];
141		let lcl = LenCachingRwLock::new(v);
142		assert_eq!(lcl.load_len(), 3);
143		lcl.write().push(4);
144		assert_eq!(lcl.load_len(), 4);
145	}
146
147	#[test]
148	fn works_with_vec() {
149		let v: Vec<i32> = Vec::new();
150		let lcl = LenCachingRwLock::new(v);
151		assert!(lcl.write().is_empty());
152	}
153
154	#[test]
155	fn works_with_vecdeque() {
156		let v: VecDeque<i32> = VecDeque::new();
157		let lcl = LenCachingRwLock::new(v);
158		lcl.write().push_front(4);
159		assert_eq!(lcl.load_len(), 1);
160	}
161
162	#[test]
163	fn read_works() {
164		let v = vec![1,2,3];
165		let lcl = LenCachingRwLock::new(v);
166		assert_eq!(lcl.read().len(), 3);
167	}
168}