diff options
| -rwxr-xr-x | src/collection/owned.rs | 14 | ||||
| -rw-r--r-- | src/iterator.rs | 18 | ||||
| -rw-r--r-- | src/iterator/consumed_guard.rs | 58 | ||||
| -rw-r--r-- | src/iterator/guard.rs | 58 | ||||
| -rw-r--r-- | src/iterator/iterator.rs | 109 |
5 files changed, 256 insertions, 1 deletions
diff --git a/src/collection/owned.rs b/src/collection/owned.rs index 2172b96..516f1ea 100755 --- a/src/collection/owned.rs +++ b/src/collection/owned.rs @@ -647,9 +647,21 @@ impl<L: OwnedLockable> OwnedLockCollection<L> where for<'a> &'a L: IntoIterator, { - pub fn locking_iter(&self, key: ThreadKey) -> LockingIterator<<&L as IntoIterator>::IntoIter> { + pub fn locking_iter( + &self, + key: ThreadKey, + ) -> LockingIterator<<&L as IntoIterator>::IntoIter, ThreadKey> { LockingIterator::new(key, (&self.child).into_iter()) } + + pub fn scoped_locking_iter<Key: Keyable, R>( + &self, + key: Key, + f: impl FnOnce(&mut LockingIterator<<&L as IntoIterator>::IntoIter, Key>) -> R, + ) -> R { + let mut iterator = LockingIterator::new(key, (&self.child).into_iter()); + f(&mut iterator) + } } #[cfg(test)] diff --git a/src/iterator.rs b/src/iterator.rs new file mode 100644 index 0000000..30b01e0 --- /dev/null +++ b/src/iterator.rs @@ -0,0 +1,18 @@ +mod consumed_guard; +mod guard; +mod iterator; + +pub struct LockingIterator<L, Key> { + key: Key, + lockable: L, +} + +pub struct IteratorGuard<'a, Guard, Key> { + _key: &'a Key, + guard: Guard, +} + +pub struct ConsumedIteratorGuard<Guard, Key> { + key: Key, + guard: Guard, +} diff --git a/src/iterator/consumed_guard.rs b/src/iterator/consumed_guard.rs new file mode 100644 index 0000000..5331289 --- /dev/null +++ b/src/iterator/consumed_guard.rs @@ -0,0 +1,58 @@ +use std::fmt::{Debug, Display}; +use std::hash::Hash; +use std::ops::{Deref, DerefMut}; + +use crate::iterator::ConsumedIteratorGuard; + +#[mutants::skip] // hashing involves RNG and is hard to test +#[cfg(not(tarpaulin_include))] +impl<Guard: Hash, Key> Hash for ConsumedIteratorGuard<Guard, Key> { + fn hash<H: std::hash::Hasher>(&self, state: &mut H) { + self.guard.hash(state) + } +} + +// No implementations of Eq, PartialEq, PartialOrd, or Ord +// You can't implement both PartialEq<Self> and PartialEq<T> +// It's easier to just implement neither and ask users to dereference +// This is less of a problem when using the scoped lock API + +#[mutants::skip] +#[cfg(not(tarpaulin_include))] +impl<Guard: Debug, Key> Debug for ConsumedIteratorGuard<Guard, Key> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Debug::fmt(&**self, f) + } +} + +impl<Guard: Display, Key> Display for ConsumedIteratorGuard<Guard, Key> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Display::fmt(&**self, f) + } +} + +impl<Guard, Key> Deref for ConsumedIteratorGuard<Guard, Key> { + type Target = Guard; + + fn deref(&self) -> &Self::Target { + &self.guard + } +} + +impl<Guard, Key> DerefMut for ConsumedIteratorGuard<Guard, Key> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.guard + } +} + +impl<Guard, Key> AsRef<Guard> for ConsumedIteratorGuard<Guard, Key> { + fn as_ref(&self) -> &Guard { + &self.guard + } +} + +impl<Guard, Key> AsMut<Guard> for ConsumedIteratorGuard<Guard, Key> { + fn as_mut(&mut self) -> &mut Guard { + &mut self.guard + } +} diff --git a/src/iterator/guard.rs b/src/iterator/guard.rs new file mode 100644 index 0000000..6393fc2 --- /dev/null +++ b/src/iterator/guard.rs @@ -0,0 +1,58 @@ +use std::fmt::{Debug, Display}; +use std::hash::Hash; +use std::ops::{Deref, DerefMut}; + +use super::{ConsumedIteratorGuard, IteratorGuard}; + +#[mutants::skip] // hashing involves RNG and is hard to test +#[cfg(not(tarpaulin_include))] +impl<Guard: Hash, Key> Hash for IteratorGuard<'_, Guard, Key> { + fn hash<H: std::hash::Hasher>(&self, state: &mut H) { + self.guard.hash(state) + } +} + +// No implementations of Eq, PartialEq, PartialOrd, or Ord +// You can't implement both PartialEq<Self> and PartialEq<T> +// It's easier to just implement neither and ask users to dereference +// This is less of a problem when using the scoped lock API + +#[mutants::skip] +#[cfg(not(tarpaulin_include))] +impl<Guard: Debug, Key> Debug for IteratorGuard<'_, Guard, Key> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Debug::fmt(&**self, f) + } +} + +impl<Guard: Display, Key> Display for IteratorGuard<'_, Guard, Key> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Display::fmt(&**self, f) + } +} + +impl<Guard, Key> Deref for IteratorGuard<'_, Guard, Key> { + type Target = Guard; + + fn deref(&self) -> &Self::Target { + &self.guard + } +} + +impl<Guard, Key> DerefMut for IteratorGuard<'_, Guard, Key> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.guard + } +} + +impl<Guard, Key> AsRef<Guard> for IteratorGuard<'_, Guard, Key> { + fn as_ref(&self) -> &Guard { + &self.guard + } +} + +impl<Guard, Key> AsMut<Guard> for IteratorGuard<'_, Guard, Key> { + fn as_mut(&mut self) -> &mut Guard { + &mut self.guard + } +} diff --git a/src/iterator/iterator.rs b/src/iterator/iterator.rs new file mode 100644 index 0000000..1e389a4 --- /dev/null +++ b/src/iterator/iterator.rs @@ -0,0 +1,109 @@ +use std::iter::{Enumerate, Fuse, Skip, Take}; + +use super::{ConsumedIteratorGuard, IteratorGuard, LockingIterator}; + +use crate::{ + lockable::{Lockable, RawLock, Sharable}, + Keyable, +}; + +impl<L, Key> LockingIterator<L, Key> { + pub(crate) const fn new(key: Key, lockable: L) -> Self { + Self { key, lockable } + } + + fn with_iterator<M>(self, f: impl FnOnce(L) -> M) -> LockingIterator<M, Key> { + LockingIterator { + key: self.key, + lockable: f(self.lockable), + } + } +} + +impl<'a, I: Iterator<Item = &'a L>, L: 'a + Lockable + RawLock, Key: Keyable> + LockingIterator<I, Key> +{ + pub fn lock_next(&'a mut self) -> Option<IteratorGuard<'a, <L as Lockable>::Guard<'a>, Key>> { + if let Some(lock) = self.lockable.next() { + unsafe { + lock.raw_write(); + let guard = lock.guard(); + + Some(IteratorGuard { + _key: &self.key, + guard, + }) + } + } else { + None + } + } + + pub fn lock_last(self) -> Option<ConsumedIteratorGuard<<L as Lockable>::Guard<'a>, Key>> { + self.lockable.last().map(|lock| unsafe { + lock.raw_write(); + let guard = lock.guard(); + + ConsumedIteratorGuard { + key: self.key, + guard, + } + }) + } +} + +impl<'a, I: Iterator<Item = &'a L>, L: 'a + Sharable + RawLock, Key: Keyable> + LockingIterator<I, Key> +{ + pub fn read_next( + &'a mut self, + ) -> Option<IteratorGuard<'a, <L as Sharable>::ReadGuard<'a>, Key>> { + if let Some(lock) = self.lockable.next() { + unsafe { + lock.raw_read(); + let guard = lock.read_guard(); + + Some(IteratorGuard { + _key: &self.key, + guard, + }) + } + } else { + None + } + } + + pub fn read_last(self) -> Option<ConsumedIteratorGuard<<L as Sharable>::ReadGuard<'a>, Key>> { + self.lockable.last().map(|lock| unsafe { + lock.raw_read(); + let guard = lock.read_guard(); + + ConsumedIteratorGuard { + key: self.key, + guard, + } + }) + } +} + +impl<L: Iterator, Key> LockingIterator<L, Key> { + pub fn size_hint(&self) -> (usize, Option<usize>) { + self.lockable.size_hint() + } + + pub fn enumerate(self) -> LockingIterator<Enumerate<L>, Key> { + self.with_iterator(Iterator::enumerate) + } + + pub fn skip(self, n: usize) -> LockingIterator<Skip<L>, Key> { + self.with_iterator(|i| i.skip(n)) + } + + pub fn take(self, n: usize) -> LockingIterator<Take<L>, Key> { + self.with_iterator(|i| i.take(n)) + } + + pub fn fuse(self) -> LockingIterator<Fuse<L>, Key> { + self.with_iterator(Iterator::fuse) + } +} |
