summaryrefslogtreecommitdiff
path: root/src/context/iterator.rs
blob: cc3bd7c98e1974580ff7513d874f138d59f655be (plain)
use std::{
	iter::{Fuse, Peekable, Skip, Take},
	marker::PhantomData,
};

use super::{ContextGuard, LockingIterator};

use crate::{
	context::LockingTuple,
	lockable::{Lockable, RawLock, Sharable},
	ThreadKey,
};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TryLockNextError {
	FinishedIteration,
	WouldBlock,
}

impl<'l, I, O> LockingIterator<'l, I, O> {
	fn with_iterator<M>(self, f: impl FnOnce(I) -> M) -> LockingIterator<'l, M, O> {
		LockingIterator {
			key: self.key,
			iterator: f(self.iterator),
			outer: self.outer,
		}
	}

	/// Exit out of the current scope of the locking iterator into the parent.
	///
	/// After using one the recurse methods, it is possible to regain access to
	/// the parent by exiting out of the scope of the child. Doing this will make
	/// it impossible to re-enter this scope again.
	///
	/// # Example
	///
	/// ```
	/// use happylock::{Mutex, ThreadKey};
	/// use happylock::collection::OwnedLockCollection;
	///
	/// let key = ThreadKey::get().unwrap();
	/// let data = ([Mutex::new(1), Mutex::new(2), Mutex::new(3)], Mutex::new(true));
	/// let locks = OwnedLockCollection::new(data);
	/// let mut ctx = locks.context();
	/// let tuple = ctx.tuple(key);
	/// let mut iter = tuple.recurse_0_iter();
	///
	/// let mut sum = 0;
	/// while let Some(item) = iter.lock_next() {
	///     sum += **item;
	/// }
	///
	/// let tuple = iter.exit();
	/// let (should_assert, _) = tuple.lock_1();
	/// if **should_assert {
	///     assert_eq!(sum, 6);
	/// }
	/// ```
	pub fn exit(self) -> O {
		self.outer
	}
}

impl<'c, L: Iterator<Item = I>, I: IntoIterator, O> LockingIterator<'c, L, O> {
	/// Create a new `LockingIterator` based on the next element in the iterator.
	///
	/// If a list contains a list of locks, then this method can be used to
	/// recurse into the next element of the list. To go back to the parent scope,
	/// use [`LockingIterator::exit`] on the new list.
	///
	/// # Example
	///
	/// ```
	/// use happylock::{Mutex, ThreadKey};
	/// use happylock::collection::OwnedLockCollection;
	///
	/// let key = ThreadKey::get().unwrap();
	/// let data = [
	///     [Mutex::new(1), Mutex::new(2), Mutex::new(3)],
	///     [Mutex::new(4), Mutex::new(5), Mutex::new(6)],
	/// ];
	/// let locks = OwnedLockCollection::new(data);
	/// let mut ctx = locks.context();
	/// let mut iter = ctx.iter(key);
	///
	/// let mut sums = Vec::new();
	/// while let Some(mut list) = iter.recurse_next() {
	///     let mut sum = 0;
	///     while let Some(item) = list.lock_next() {
	///         sum += **item;
	///     }
	///     sums.push(sum);
	///     iter = list.exit();
	/// }
	///
	/// assert_eq!(sums, vec![6, 15]);
	/// ```
	pub fn recurse_next(
		mut self,
	) -> Option<LockingIterator<'c, <I as IntoIterator>::IntoIter, Self>> {
		if let Some(iterator) = self.iterator.next() {
			Some(LockingIterator {
				key: self.key,
				iterator: iterator.into_iter(),
				outer: self,
			})
		} else {
			None
		}
	}

	/// Create a new `LockingIterator` based on the next element in the iterator.
	///
	/// If a list contains a list of locks, then this method can be used to
	/// recurse into the next element of the list. To go back to the parent scope,
	/// use [`LockingIterator::exit`] on the new list.
	///
	/// # Example
	///
	/// ```
	/// use happylock::{Mutex, ThreadKey};
	/// use happylock::collection::OwnedLockCollection;
	///
	/// let key = ThreadKey::get().unwrap();
	/// let data = [
	///     [Mutex::new(1), Mutex::new(2), Mutex::new(3)],
	///     [Mutex::new(4), Mutex::new(5), Mutex::new(6)],
	/// ];
	/// let locks = OwnedLockCollection::new(data);
	/// let mut ctx = locks.context();
	/// let iter = ctx.iter(key);
	///
	/// let mut list = iter.recurse_last().unwrap();
	/// let mut sum = 0;
	/// while let Some(item) = list.lock_next() {
	///     sum += **item;
	/// }
	///
	/// assert_eq!(sum, 15);
	/// ```
	pub fn recurse_last(self) -> Option<LockingIterator<'c, <I as IntoIterator>::IntoIter, O>> {
		if let Some(iterator) = self.iterator.last() {
			Some(LockingIterator {
				key: self.key,
				iterator: iterator.into_iter(),
				outer: self.outer,
			})
		} else {
			None
		}
	}
}

impl<'c, L: Iterator<Item = &'c T>, T: 'c, O> LockingIterator<'c, L, O> {
	/// Create a new `LockingIterator` based on the next element in the iterator.
	///
	/// If a list contains a list of locks, then this method can be used to
	/// recurse into the next element of the list. To go back to the parent scope,
	/// use [`LockingIterator::exit`] on the new list.
	///
	/// # Example
	///
	/// ```
	/// use happylock::{Mutex, ThreadKey};
	/// use happylock::collection::OwnedLockCollection;
	///
	/// let key = ThreadKey::get().unwrap();
	/// let data = [
	///     (Mutex::new(true), Mutex::new(1)),
	///     (Mutex::new(false), Mutex::new(2)),
	///     (Mutex::new(true), Mutex::new(3)),
	/// ];
	/// let locks = OwnedLockCollection::new(data);
	/// let mut ctx = locks.context();
	/// let mut iter = ctx.iter(key);
	///
	/// let mut sum = 0;
	/// while let Some(tuple) = iter.recurse_next_tuple() {
	///     let (should_count, mut tuple) = tuple.lock_0();
	///     if **should_count {
	///         let num = tuple.lock_mut_1();
	///         sum += **num;
	///     }
	///     iter = tuple.exit();
	/// }
	///
	/// assert_eq!(sum, 4);
	/// ```
	pub fn recurse_next_tuple(mut self) -> Option<LockingTuple<'c, T, T, Self>> {
		if let Some(tuple) = self.iterator.next() {
			Some(LockingTuple {
				key: self.key,
				_lockable: PhantomData,
				tuple,
				outer: self,
			})
		} else {
			None
		}
	}

	/// Create a new `LockingIterator` based on the next element in the iterator.
	///
	/// If a list contains a list of locks, then this method can be used to
	/// recurse into the next element of the list. To go back to the parent scope,
	/// use [`LockingIterator::exit`] on the new list.
	///
	/// # Example
	///
	/// ```
	/// use happylock::{Mutex, ThreadKey};
	/// use happylock::collection::OwnedLockCollection;
	///
	/// let key = ThreadKey::get().unwrap();
	/// let data = [
	///     (Mutex::new(true), Mutex::new(1)),
	///     (Mutex::new(false), Mutex::new(2)),
	///     (Mutex::new(true), Mutex::new(3)),
	/// ];
	/// let locks = OwnedLockCollection::new(data);
	/// let mut ctx = locks.context();
	/// let iter = ctx.iter(key);
	///
	/// let tuple = iter.recurse_last_tuple().unwrap();
	/// let (should_count, mut tuple) = tuple.lock_0();
	/// if **should_count {
	///     let num = tuple.lock_mut_1();
	///     assert_eq!(**num, 3);
	/// } else {
	///     panic!();
	/// }
	/// ```
	pub fn recurse_last_tuple(self) -> Option<LockingTuple<'c, T, T, O>> {
		if let Some(tuple) = self.iterator.last() {
			Some(LockingTuple {
				key: self.key,
				_lockable: PhantomData,
				tuple,
				outer: self.outer,
			})
		} else {
			None
		}
	}
}

impl<'c, L: 'c + Iterator<Item = &'c I>, I: 'c + RawLock + Lockable, O> LockingIterator<'c, L, O> {
	/// Advances the iterator, locking the next element and returning a guard to
	/// the inner data.
	///
	/// Returns `None` when iteration is finished. Individual iterator
	/// implementations may choose to resume iteration, and so calling `next()`
	/// again may or may not eventually start returning `Some(Item)` again at some
	/// point.
	///
	/// # Example
	///
	/// ```
	/// use happylock::{Mutex, ThreadKey};
	/// use happylock::collection::OwnedLockCollection;
	///
	/// let key = ThreadKey::get().unwrap();
	/// let data = [Mutex::new(1), Mutex::new(3), Mutex::new(8)];
	/// let locks = OwnedLockCollection::new(data);
	/// let mut ctx = locks.context();
	/// let mut iter = ctx.iter(key);
	///
	/// let mut sum = 0;
	/// while let Some(item) = iter.lock_next() {
	///     sum += **item;
	/// }
	///
	/// assert_eq!(sum, 12);
	/// ```
	pub fn lock_next(&mut self) -> Option<ContextGuard<'c, <I as Lockable>::Guard<'c>, ThreadKey>> {
		if let Some(lock) = self.iterator.next() {
			unsafe {
				lock.raw_write();
				let guard = lock.guard();

				Some(ContextGuard {
					_key: self.key,
					guard,
				})
			}
		} else {
			None
		}
	}

	/// Consumes the iterator, returning the last element, without locking any
	/// other elements.
	///
	/// This method will evaluate the iterator until it returns `None`. While
	/// doing so, it keeps track of the current element. After `None` is returned,
	/// `lock_last()` will then lock the last element it saw and return the
	/// lock's data.
	///
	/// # Panics
	///
	/// This function might panic if the iterator is infinite.
	///
	/// # Example
	///
	/// ```
	/// use happylock::{Mutex, ThreadKey};
	/// use happylock::collection::OwnedLockCollection;
	///
	/// let key = ThreadKey::get().unwrap();
	/// let data = [Mutex::new(1), Mutex::new(3), Mutex::new(8)];
	/// let locks = OwnedLockCollection::new(data);
	/// let mut ctx = locks.context();
	/// let iter = ctx.iter(key);
	///
	/// let last = iter.lock_last().unwrap();
	/// assert_eq!(**last, 8);
	/// ```
	pub fn lock_last(self) -> Option<ContextGuard<'c, <I as Lockable>::Guard<'c>, ThreadKey>> {
		self.iterator.last().map(|lock| unsafe {
			lock.raw_write();
			let guard = lock.guard();

			ContextGuard {
				_key: self.key,
				guard,
			}
		})
	}
}

impl<'c, L: 'c + Iterator<Item = &'c I>, I: 'c + RawLock + Lockable, O>
	LockingIterator<'c, Peekable<L>, O>
{
	/// Attempts to lock the next element and returning a guard to
	/// the inner data.
	///
	/// # Errors
	///
	/// Returns `Err(TryLockNextError::FinishedIteration)` when iteration is
	/// finished. Individual iterator implementations may choose to resume
	/// iteration, and so calling `next()` again may or may not eventually start
	/// returning `Some(Item)` again at some point.
	///
	/// Returns `Err(TryLockNextError::WouldBlock)` if the next lock in the
	/// iterator is already locked. This will not advance the iterator.
	///
	/// # Example
	///
	/// ```
	/// use happylock::{Mutex, ThreadKey};
	/// use happylock::collection::OwnedLockCollection;
	/// use happylock::context::iterator::TryLockNextError;
	///
	/// let key = ThreadKey::get().unwrap();
	/// let data = [Mutex::new(1), Mutex::new(3), Mutex::new(8)];
	/// let locks = OwnedLockCollection::new(data);
	/// let mut ctx = locks.context();
	/// let mut iter = ctx.iter(key).peekable();
	///
	/// let mut sum = 0;
	/// loop {
	///     match iter.try_lock_next() {
	///         Ok(item) => sum += **item,
	///         Err(TryLockNextError::WouldBlock) => continue,
	///         Err(TryLockNextError::FinishedIteration) => break,
	///     }
	/// }
	///
	/// assert_eq!(sum, 12);
	/// ```
	pub fn try_lock_next(
		&mut self,
	) -> Result<ContextGuard<'c, <I as Lockable>::Guard<'c>, ThreadKey>, TryLockNextError> {
		if let Some(lock) = self.iterator.peek().copied() {
			unsafe {
				if lock.raw_try_write() {
					// safety: we just saw that there is a valid value
					let lock = self.iterator.next().unwrap_unchecked();
					let guard = lock.guard();

					Ok(ContextGuard {
						_key: self.key,
						guard,
					})
				} else {
					Err(TryLockNextError::WouldBlock)
				}
			}
		} else {
			Err(TryLockNextError::FinishedIteration)
		}
	}
}

impl<'c, L: 'c + Iterator<Item = &'c I>, I: 'c + RawLock + Sharable, O> LockingIterator<'c, L, O> {
	/// Advances the iterator, acquiring a shared lock to the next element and
	/// returning a guard to the inner data.
	///
	/// Returns `None` when iteration is finished. Individual iterator
	/// implementations may choose to resume iteration, and so calling `next()`
	/// again may or may not eventually start returning `Some(Item)` again at some
	/// point.
	///
	/// # Example
	///
	/// ```
	/// use happylock::{RwLock, ThreadKey};
	/// use happylock::collection::OwnedLockCollection;
	///
	/// let key = ThreadKey::get().unwrap();
	/// let data = [RwLock::new(1), RwLock::new(3), RwLock::new(8)];
	/// let locks = OwnedLockCollection::new(data);
	/// let mut ctx = locks.context();
	/// let mut iter = ctx.iter(key);
	///
	/// let mut sum = 0;
	/// while let Some(item) = iter.read_next() {
	///     sum += **item;
	/// }
	///
	/// assert_eq!(sum, 12);
	/// ```
	pub fn read_next(
		&mut self,
	) -> Option<ContextGuard<'c, <I as Sharable>::ReadGuard<'c>, ThreadKey>> {
		if let Some(lock) = self.iterator.next() {
			unsafe {
				lock.raw_read();
				let guard = lock.read_guard();

				Some(ContextGuard {
					_key: self.key,
					guard,
				})
			}
		} else {
			None
		}
	}

	/// Consumes the iterator, returning the last element with readonly access,
	/// without locking any other elements.
	///
	/// This method will evaluate the iterator until it returns `None`. While
	/// doing so, it keeps track of the current element. After `None` is returned,
	/// `lock_last()` will then lock the last element it saw and return the
	/// lock's data.
	///
	/// # Panics
	///
	/// This function might panic if the iterator is infinite.
	///
	/// # Example
	///
	/// ```
	/// use happylock::{RwLock, ThreadKey};
	/// use happylock::collection::OwnedLockCollection;
	///
	/// let key = ThreadKey::get().unwrap();
	/// let data = [RwLock::new(1), RwLock::new(3), RwLock::new(8)];
	/// let locks = OwnedLockCollection::new(data);
	/// let mut ctx = locks.context();
	/// let iter = ctx.iter(key);
	///
	/// let last = iter.read_last().unwrap();
	/// assert_eq!(**last, 8);
	/// ```
	pub fn read_last(self) -> Option<ContextGuard<'c, <I as Sharable>::ReadGuard<'c>, ThreadKey>> {
		self.iterator.last().map(|lock| unsafe {
			lock.raw_read();
			let guard = lock.read_guard();

			ContextGuard {
				_key: self.key,
				guard,
			}
		})
	}
}

impl<'c, L: 'c + Iterator<Item = &'c I>, I: 'c + RawLock + Sharable, O>
	LockingIterator<'c, Peekable<L>, O>
{
	/// Attempts to acquire a shared lock the next element and returning a guard
	/// to the inner data.
	///
	/// # Errors
	///
	/// Returns `Err(TryLockNextError::FinishedIteration)` when iteration is
	/// finished. Individual iterator implementations may choose to resume
	/// iteration, and so calling `next()` again may or may not eventually start
	/// returning `Some(Item)` again at some point.
	///
	/// Returns `Err(TryLockNextError::WouldBlock)` if the next lock in the
	/// iterator is already locked. This will not advance the iterator.
	///
	/// # Example
	///
	/// ```
	/// use happylock::{RwLock, ThreadKey};
	/// use happylock::collection::OwnedLockCollection;
	/// use happylock::context::iterator::TryLockNextError;
	///
	/// let key = ThreadKey::get().unwrap();
	/// let data = [RwLock::new(1), RwLock::new(3), RwLock::new(8)];
	/// let locks = OwnedLockCollection::new(data);
	/// let mut ctx = locks.context();
	/// let mut iter = ctx.iter(key).peekable();
	///
	/// let mut sum = 0;
	/// loop {
	///     match iter.try_read_next() {
	///         Ok(item) => sum += **item,
	///         Err(TryLockNextError::WouldBlock) => continue,
	///         Err(TryLockNextError::FinishedIteration) => break,
	///     }
	/// }
	///
	/// assert_eq!(sum, 12);
	/// ```
	pub fn try_read_next(
		&mut self,
	) -> Result<ContextGuard<'c, <I as Sharable>::ReadGuard<'c>, ThreadKey>, TryLockNextError> {
		if let Some(lock) = self.iterator.peek().copied() {
			unsafe {
				if lock.raw_try_read() {
					// safety: we just saw that there is a valid value
					let lock = self.iterator.next().unwrap_unchecked();
					let guard = lock.read_guard();

					Ok(ContextGuard {
						_key: self.key,
						guard,
					})
				} else {
					Err(TryLockNextError::WouldBlock)
				}
			}
		} else {
			Err(TryLockNextError::FinishedIteration)
		}
	}
}

impl<'l, L: Iterator, O> LockingIterator<'l, L, O> {
	/// Advances the iterator, without locking the next element in the iterator.
	///
	/// Returns `false` when iteration is finished. Individual iterator
	/// implementations may choose to resume iteration, and so calling
	/// `skip_next()` again may or may not eventually start returning `true` again
	/// at some point.
	///
	/// # Example
	///
	/// ```
	/// use happylock::{Mutex, ThreadKey};
	/// use happylock::collection::OwnedLockCollection;
	///
	/// let key = ThreadKey::get().unwrap();
	/// let data = [Mutex::new(1), Mutex::new(3), Mutex::new(8)];
	/// let locks = OwnedLockCollection::new(data);
	/// let mut ctx = locks.context();
	/// let mut iter = ctx.iter(key);
	///
	/// iter.skip_next();
	/// assert!(iter.lock_next().is_some_and(|v| **v == 3));
	/// ```
	pub fn skip_next(&mut self) -> bool {
		self.iterator.next().is_some()
	}

	/// Advances the iterator, skipping `n` elements without locking.
	///
	/// See [`Iterator::skip`] for more information.
	///
	/// # Example
	///
	/// ```
	/// use happylock::{Mutex, ThreadKey};
	/// use happylock::collection::OwnedLockCollection;
	///
	/// let key = ThreadKey::get().unwrap();
	/// let data = [Mutex::new(1), Mutex::new(3), Mutex::new(8)];
	/// let locks = OwnedLockCollection::new(data);
	/// let mut ctx = locks.context();
	/// let mut iter = ctx.iter(key);
	///
	/// iter.skip_mut(2);
	/// assert!(iter.lock_next().is_some_and(|v| **v == 8));
	/// ```
	pub fn skip_mut(&mut self, n: usize) {
		for _ in 0..n {
			self.iterator.next();
		}
	}

	/// Returns the bounds on the remaining length of the iterator.
	///
	/// See [`Iterator::size_hint`] for more information.
	///
	/// # Example
	///
	/// ```
	/// use happylock::{Mutex, ThreadKey};
	/// use happylock::collection::OwnedLockCollection;
	///
	/// let key = ThreadKey::get().unwrap();
	/// let data = [Mutex::new(1), Mutex::new(2), Mutex::new(3)];
	/// let locks = OwnedLockCollection::new(data);
	/// let mut ctx = locks.context();
	/// let mut iter = ctx.iter(key);
	///
	/// assert_eq!((3, Some(3)), iter.size_hint());
	/// let _ = iter.skip_next();
	/// assert_eq!((2, Some(2)), iter.size_hint());
	/// ```
	#[must_use]
	pub fn size_hint(&self) -> (usize, Option<usize>) {
		self.iterator.size_hint()
	}

	/// Creates a new [`LockingIterator`] that skips the first `n` elements.
	///
	/// Unlike `skip_next` or `skip_mut`, this method does not modify the iterator
	/// in place. Instead, it returns a new iterator which skips the first `n`
	/// elements.
	///
	/// See [`Iterator::skip`] for more information.
	///
	/// # Example
	///
	/// ```
	/// use happylock::{Mutex, ThreadKey};
	/// use happylock::collection::OwnedLockCollection;
	///
	/// let key = ThreadKey::get().unwrap();
	/// let data = [Mutex::new(1), Mutex::new(2), Mutex::new(3)];
	/// let locks = OwnedLockCollection::new(data);
	/// let mut ctx = locks.context();
	/// let mut iter = ctx.iter(key).skip(2);
	///
	/// assert!(iter.lock_next().is_some_and(|v| **v == 3));
	/// assert!(iter.lock_next().is_none());
	/// ```
	#[must_use]
	pub fn skip(self, n: usize) -> LockingIterator<'l, Skip<L>, O> {
		self.with_iterator(|i| i.skip(n))
	}

	/// Creates a new [`LockingIterator`] that yields only the first `n` elements,
	/// or fewer if the iterator ends sooner.
	///
	/// See [`Iterator::take`] for more information.
	///
	/// # Example
	///
	/// ```
	/// use happylock::{Mutex, ThreadKey};
	/// use happylock::collection::OwnedLockCollection;
	///
	/// let key = ThreadKey::get().unwrap();
	/// let data = [Mutex::new(1), Mutex::new(2), Mutex::new(3)];
	/// let locks = OwnedLockCollection::new(data);
	/// let mut ctx = locks.context();
	/// let mut iter = ctx.iter(key).take(2);
	///
	/// assert!(iter.lock_next().is_some_and(|v| **v == 1));
	/// assert!(iter.lock_next().is_some_and(|v| **v == 2));
	/// assert!(iter.lock_next().is_none());
	/// ```
	#[must_use]
	pub fn take(self, n: usize) -> LockingIterator<'l, Take<L>, O> {
		self.with_iterator(|i| i.take(n))
	}

	/// Creates a new [`LockingIterator`] that ends after the first `None`
	///
	/// See [`Iterator::fuse`] for more information
	#[must_use]
	pub fn fuse(self) -> LockingIterator<'l, Fuse<L>, O> {
		self.with_iterator(Iterator::fuse)
	}

	/// Creates a new [`LockingIterator`] which has access to the
	/// [`try_lock_next`] and/or [`try_read_next`] methods.
	///
	/// See [`Iterator::peekable`] for more information
	///
	/// [`try_lock_next`]: `LockingIterator::try_lock_next`
	/// [`try_read_next`]: `LockingIterator::try_read_next`
	#[must_use]
	pub fn peekable(self) -> LockingIterator<'l, Peekable<L>, O> {
		self.with_iterator(Iterator::peekable)
	}
}