summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xsrc/collection/owned.rs14
-rw-r--r--src/iterator.rs18
-rw-r--r--src/iterator/consumed_guard.rs58
-rw-r--r--src/iterator/guard.rs58
-rw-r--r--src/iterator/iterator.rs109
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)
+ }
+}