summaryrefslogtreecommitdiff
path: root/src/context
diff options
context:
space:
mode:
Diffstat (limited to 'src/context')
-rw-r--r--src/context/context.rs158
-rw-r--r--src/context/guard.rs58
-rw-r--r--src/context/iterator.rs695
-rw-r--r--src/context/tuple.rs847
4 files changed, 1758 insertions, 0 deletions
diff --git a/src/context/context.rs b/src/context/context.rs
new file mode 100644
index 0000000..adc684f
--- /dev/null
+++ b/src/context/context.rs
@@ -0,0 +1,158 @@
+use std::marker::PhantomData;
+
+use crate::{
+ context::{LockContext, LockingIterator, LockingTuple},
+ lockable::{Lockable, OwnedLockable},
+ ThreadKey,
+};
+
+impl<'l, L> LockContext<'l, L> {
+ pub(crate) const fn new(lockable: &'l L) -> Self
+ where
+ L: OwnedLockable,
+ {
+ Self {
+ key: None,
+ lockable,
+ }
+ }
+
+ /// Unlocks all locks in the collection, returning the [`ThreadKey`].
+ ///
+ /// This requires a mutable reference to the context, so it cannot be called
+ /// without first dropping any [`ContextGuard`]s that reference this context.
+ /// This method will also return `None` if the context has not been locked
+ /// with a `ThreadKey`.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use happylock::{Mutex, ThreadKey};
+ /// use happylock::collection::OwnedLockCollection;
+ ///
+ /// let key = ThreadKey::get().unwrap();
+ /// let data = (Mutex::new(42), Mutex::new(true));
+ /// let locks = OwnedLockCollection::new(data);
+ /// let mut ctx = locks.context();
+ /// let tuple = ctx.tuple(key);
+ ///
+ /// let (use_other, tuple) = tuple.lock_1();
+ /// if **use_other {
+ /// drop(use_other);
+ /// drop(tuple);
+ /// let key = ctx.unlock().unwrap();
+ /// let tuple = ctx.tuple(key);
+ /// let (mut item, _) = tuple.lock_0();
+ /// **item = 67;
+ /// } else {
+ /// drop(use_other);
+ /// drop(tuple);
+ /// };
+ ///
+ /// let key = ctx.unlock().unwrap();
+ /// let tuple = ctx.tuple(key);
+ /// let (number, _) = tuple.lock_0();
+ /// assert_eq!(**number, 67);
+ /// ```
+ ///
+ /// [`ContextGuard`]: `crate::context::ContextGuard`
+ pub fn unlock(&mut self) -> Option<ThreadKey> {
+ self.key.take()
+ }
+}
+
+impl<L: Lockable> LockContext<'_, L> {
+ /// Creates a [`LockingTuple`], which can lock a subset of a tuple of locks,
+ /// in a specific order.
+ ///
+ /// Sometimes, partial allocation of locks is useful. For example, you may want
+ /// to acquire a lock on one item before deciding if the second item should be
+ /// locked. If the locks can be organized into a tuple, [`LockingTuple`] is
+ /// capable of doing exactly that.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use happylock::{Mutex, ThreadKey};
+ /// use happylock::collection::OwnedLockCollection;
+ ///
+ /// let key = ThreadKey::get().unwrap();
+ /// let data = (Mutex::new(true), Mutex::new(42), Mutex::new(67));
+ /// let locks = OwnedLockCollection::new(data);
+ /// let mut ctx = locks.context();
+ /// let tuple = ctx.tuple(key);
+ ///
+ /// let (use_other, tuple) = tuple.lock_0();
+ /// let number = if **use_other {
+ /// tuple.lock_2().0
+ /// } else {
+ /// tuple.lock_1().0
+ /// };
+ /// assert_eq!(**number, 67);
+ /// ```
+ pub fn tuple(&mut self, key: ThreadKey) -> LockingTuple<'_, L, L> {
+ unsafe {
+ self.key = Some(key);
+
+ LockingTuple {
+ _lockable: PhantomData,
+ // safety: we just inserted a key
+ key: self.key.as_ref().unwrap_unchecked(),
+ tuple: self.lockable,
+ outer: (),
+ }
+ }
+ }
+}
+
+impl<'l, L> LockContext<'l, L>
+where
+ &'l L: IntoIterator,
+{
+ /// Creates a [`LockingIterator`] to iterate through a collection of locks
+ /// without locking everything at once.
+ ///
+ /// Sometimes, partial allocation of locks is useful. For example, you may
+ /// want to acquire a lock on the first element of a list before deciding if
+ /// the second element should be locked. If the list is iterable, then a
+ /// [`LockingIterator`] is capable of doing exactly that.
+ ///
+ /// # 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);
+ /// ```
+ // TODO: support scoped locks
+ // TODO: implement get_disjoint
+ // TODO: support some sort of index tower thing
+ #[expect(clippy::iter_not_returning_iterator)]
+ pub fn iter(
+ &mut self,
+ key: ThreadKey,
+ ) -> LockingIterator<'_, <&'l L as IntoIterator>::IntoIter> {
+ unsafe {
+ self.key = Some(key);
+
+ LockingIterator {
+ // safety: we just inserted a key
+ key: self.key.as_ref().unwrap_unchecked(),
+ iterator: self.lockable.into_iter(),
+ outer: (),
+ }
+ }
+ }
+}
diff --git a/src/context/guard.rs b/src/context/guard.rs
new file mode 100644
index 0000000..0898c1f
--- /dev/null
+++ b/src/context/guard.rs
@@ -0,0 +1,58 @@
+use std::fmt::{Debug, Display};
+use std::hash::Hash;
+use std::ops::{Deref, DerefMut};
+
+use super::ContextGuard;
+
+#[mutants::skip] // hashing involves RNG and is hard to test
+#[cfg(not(tarpaulin_include))]
+impl<Guard: Hash, Key> Hash for ContextGuard<'_, 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 ContextGuard<'_, Guard, Key> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ Debug::fmt(&**self, f)
+ }
+}
+
+impl<Guard: Display, Key> Display for ContextGuard<'_, Guard, Key> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ Display::fmt(&**self, f)
+ }
+}
+
+impl<Guard, Key> Deref for ContextGuard<'_, Guard, Key> {
+ type Target = Guard;
+
+ fn deref(&self) -> &Self::Target {
+ &self.guard
+ }
+}
+
+impl<Guard, Key> DerefMut for ContextGuard<'_, Guard, Key> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.guard
+ }
+}
+
+impl<Guard, Key> AsRef<Guard> for ContextGuard<'_, Guard, Key> {
+ fn as_ref(&self) -> &Guard {
+ &self.guard
+ }
+}
+
+impl<Guard, Key> AsMut<Guard> for ContextGuard<'_, Guard, Key> {
+ fn as_mut(&mut self) -> &mut Guard {
+ &mut self.guard
+ }
+}
diff --git a/src/context/iterator.rs b/src/context/iterator.rs
new file mode 100644
index 0000000..cc3bd7c
--- /dev/null
+++ b/src/context/iterator.rs
@@ -0,0 +1,695 @@
+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)
+ }
+}
diff --git a/src/context/tuple.rs b/src/context/tuple.rs
new file mode 100644
index 0000000..9bca72e
--- /dev/null
+++ b/src/context/tuple.rs
@@ -0,0 +1,847 @@
+use std::marker::PhantomData;
+
+use crate::{
+ context::{ContextGuard, LockingIterator, LockingTuple},
+ lockable::{Lockable, RawLock, Sharable},
+ ThreadKey,
+};
+
+impl<'c, A, B, O> LockingTuple<'c, A, B, O> {
+ fn transmute<C>(self) -> LockingTuple<'c, C, B, O> {
+ LockingTuple {
+ _lockable: PhantomData,
+ key: self.key,
+ tuple: self.tuple,
+ outer: self.outer,
+ }
+ }
+}
+
+macro_rules! lock_impl {
+ ($self: expr, $field: tt) => {
+ unsafe {
+ $self.tuple.$field.raw_write();
+ (
+ ContextGuard {
+ _key: &$self.key,
+ guard: $self.tuple.$field.guard(),
+ },
+ $self.transmute(),
+ )
+ }
+ };
+}
+
+macro_rules! try_lock_impl {
+ ($self: expr, $field: tt) => {
+ unsafe {
+ if $self.tuple.$field.raw_try_write() {
+ Ok((
+ ContextGuard {
+ _key: $self.key,
+ guard: $self.tuple.$field.guard(),
+ },
+ $self.transmute(),
+ ))
+ } else {
+ Err($self)
+ }
+ }
+ };
+}
+
+macro_rules! lock_mut_impl {
+ ($self: expr, $field: tt) => {
+ unsafe {
+ $self.tuple.$field.raw_write();
+ ContextGuard {
+ _key: $self.key,
+ guard: $self.tuple.$field.guard(),
+ }
+ }
+ };
+}
+
+macro_rules! try_lock_mut_impl {
+ ($self: expr, $field: tt) => {
+ unsafe {
+ if $self.tuple.$field.raw_try_write() {
+ Some(ContextGuard {
+ _key: $self.key,
+ guard: $self.tuple.$field.guard(),
+ })
+ } else {
+ None
+ }
+ }
+ };
+}
+
+macro_rules! read_impl {
+ ($self: expr, $field: tt) => {
+ unsafe {
+ $self.tuple.$field.raw_read();
+ (
+ ContextGuard {
+ _key: &$self.key,
+ guard: $self.tuple.$field.read_guard(),
+ },
+ $self.transmute(),
+ )
+ }
+ };
+}
+
+macro_rules! try_read_impl {
+ ($self: expr, $field: tt) => {
+ unsafe {
+ if $self.tuple.$field.raw_try_read() {
+ Ok((
+ ContextGuard {
+ _key: $self.key,
+ guard: $self.tuple.$field.read_guard(),
+ },
+ $self.transmute(),
+ ))
+ } else {
+ Err($self)
+ }
+ }
+ };
+}
+
+macro_rules! read_mut_impl {
+ ($self: expr, $field: tt) => {
+ unsafe {
+ $self.tuple.$field.raw_read();
+ ContextGuard {
+ _key: $self.key,
+ guard: $self.tuple.$field.read_guard(),
+ }
+ }
+ };
+}
+
+macro_rules! try_read_mut_impl {
+ ($self: expr, $field: tt) => {
+ unsafe {
+ if $self.tuple.$field.raw_try_read() {
+ Some(ContextGuard {
+ _key: $self.key,
+ guard: $self.tuple.$field.read_guard(),
+ })
+ } else {
+ None
+ }
+ }
+ };
+}
+
+macro_rules! recurse_impl {
+ ($self: expr, $field: tt) => {
+ LockingTuple {
+ _lockable: PhantomData,
+ key: $self.key,
+ tuple: &$self.tuple.$field,
+ outer: $self.transmute(),
+ }
+ };
+}
+
+macro_rules! recurse_iter_impl {
+ ($self: expr, $field: tt) => {
+ LockingIterator {
+ key: $self.key,
+ iterator: $self.tuple.$field.into_iter(),
+ outer: $self.transmute(),
+ }
+ };
+}
+
+type LockReturn<'a, 'context, Guarded, L, C, O> = (
+ ContextGuard<'a, <Guarded as Lockable>::Guard<'a>, ThreadKey>,
+ LockingTuple<'context, L, C, O>,
+);
+
+type TryLockReturn<'a, 'context, Guarded, L, C, O, This> =
+ Result<LockReturn<'a, 'context, Guarded, L, C, O>, This>;
+
+type ReadReturn<'a, 'context, Guarded, L, C, O> = (
+ ContextGuard<'a, <Guarded as Sharable>::ReadGuard<'a>, ThreadKey>,
+ LockingTuple<'context, L, C, O>,
+);
+
+type TryReadReturn<'a, 'context, Guarded, L, C, O, This> =
+ Result<ReadReturn<'a, 'context, Guarded, L, C, O>, This>;
+
+type RecurseReturn<'context, Inner, L, C, O> =
+ LockingTuple<'context, Inner, Inner, LockingTuple<'context, L, C, O>>;
+
+type RecurseIterReturn<'context, Inner, L, C, O> = LockingIterator<
+ 'context,
+ <&'context Inner as IntoIterator>::IntoIter,
+ LockingTuple<'context, L, C, O>,
+>;
+
+impl<T, C, Outer> LockingTuple<'_, T, C, Outer> {
+ /// Exit out of the current scope of the locking tuple into the parent.
+ pub fn exit(self) -> Outer {
+ self.outer
+ }
+}
+
+impl<'context, A: RawLock + Lockable, Outer> LockingTuple<'context, (A,), (A,), Outer> {
+ /// Lock the first element, and return a new tuple where the first element
+ /// is inaccessible.
+ #[must_use]
+ pub fn lock_0<'a>(self) -> LockReturn<'a, 'context, A, ((),), (A,), Outer>
+ where
+ 'context: 'a,
+ {
+ lock_impl!(self, 0)
+ }
+
+ /// Attempts to lock the first element without blocking, and return a new tuple
+ /// where the first element is inaccessible.
+ ///
+ /// # Errors
+ ///
+ /// If the element is already locked, `Err` is returned with the original
+ /// tuple.
+ pub fn try_lock_0<'a>(self) -> TryLockReturn<'a, 'context, A, ((),), (A,), Outer, Self>
+ where
+ 'context: 'a,
+ {
+ try_lock_impl!(self, 0)
+ }
+
+ /// Lock the first element. The tuple becomes unusable until the returned
+ /// guard is dropped.
+ #[must_use]
+ pub fn lock_mut_0(&mut self) -> ContextGuard<'_, A::Guard<'_>, ThreadKey> {
+ lock_mut_impl!(self, 0)
+ }
+
+ /// Attempts to lock the first element without blocking. If successful, the
+ /// tuple becomes unusable until the returned guard is dropped. If the element
+ /// is already locked, `None` is returned.
+ #[must_use]
+ pub fn try_lock_mut_0(&mut self) -> Option<ContextGuard<'_, A::Guard<'_>, ThreadKey>> {
+ try_lock_mut_impl!(self, 0)
+ }
+}
+
+impl<'context, A: RawLock + Sharable, Outer> LockingTuple<'context, (A,), (A,), Outer> {
+ /// Acquire a shared lock to the first element, and return a new tuple where
+ /// the first element is inaccessible.
+ #[must_use]
+ pub fn read_0<'a>(self) -> ReadReturn<'a, 'context, A, ((),), (A,), Outer>
+ where
+ 'context: 'a,
+ {
+ read_impl!(self, 0)
+ }
+
+ /// Attempts to acquire a shared lock the first element without blocking, and
+ /// return a new tuple where the first element is inaccessible.
+ ///
+ /// # Errors
+ ///
+ /// If the element is already exclusively locked, `Err` is returned with the original
+ /// tuple.
+ pub fn try_read_0<'a>(self) -> TryReadReturn<'a, 'context, A, ((),), (A,), Outer, Self>
+ where
+ 'context: 'a,
+ {
+ try_read_impl!(self, 0)
+ }
+
+ /// Acquire a shared lock to the first element. The tuple becomes unusable
+ /// until the returned guard is dropped.
+ #[must_use]
+ pub fn read_mut_0(&mut self) -> ContextGuard<'_, A::ReadGuard<'_>, ThreadKey> {
+ read_mut_impl!(self, 0)
+ }
+
+ /// Attempts to acquire a shared lock the first element without blocking. If
+ /// successful, the tuple becomes unusable until the returned guard is
+ /// dropped. If the element is already exclusively locked, `None` is returned.
+ #[must_use]
+ pub fn try_read_mut_0(&mut self) -> Option<ContextGuard<'_, A::ReadGuard<'_>, ThreadKey>> {
+ try_read_mut_impl!(self, 0)
+ }
+}
+
+impl<'context, A, O> LockingTuple<'context, (A,), (A,), O> {
+ /// Consume the tuple, and return the first element as a new tuple.
+ #[must_use]
+ pub fn recurse_0(self) -> RecurseReturn<'context, A, ((),), (A,), O> {
+ recurse_impl!(self, 0)
+ }
+
+ /// Consume the tuple, and return the first element as a locking iterator.
+ pub fn recurse_0_iter(self) -> RecurseIterReturn<'context, A, ((),), (A,), O>
+ where
+ &'context A: IntoIterator,
+ {
+ recurse_iter_impl!(self, 0)
+ }
+}
+
+impl<'context, A: RawLock + Lockable, B, B0, O> LockingTuple<'context, (A, B), (A, B0), O> {
+ /// Lock the first element, and return a new tuple where the first element
+ /// is inaccessible.
+ #[must_use]
+ pub fn lock_0<'a>(self) -> LockReturn<'a, 'context, A, ((), B), (A, B0), O>
+ where
+ 'context: 'a,
+ {
+ lock_impl!(self, 0)
+ }
+
+ /// Lock the first element. The tuple becomes unusable until the returned
+ /// guard is dropped.
+ #[must_use]
+ pub fn lock_mut_0(&mut self) -> ContextGuard<'_, A::Guard<'_>, ThreadKey> {
+ lock_mut_impl!(self, 0)
+ }
+
+ /// Attempts to lock the first element without blocking, and return a new tuple
+ /// where the first element is inaccessible.
+ ///
+ /// # Errors
+ ///
+ /// If the element is already locked, `Err` is returned with the original
+ /// tuple.
+ pub fn try_lock_0<'a>(self) -> TryLockReturn<'a, 'context, A, ((), B), (A, B0), O, Self>
+ where
+ 'context: 'a,
+ {
+ try_lock_impl!(self, 0)
+ }
+
+ /// Attempts to lock the first element without blocking. If successful, the
+ /// tuple becomes unusable until the returned guard is dropped. If the element
+ /// is already locked, `None` is returned.
+ #[must_use]
+ pub fn try_lock_mut_0(&mut self) -> Option<ContextGuard<'_, A::Guard<'_>, ThreadKey>> {
+ try_lock_mut_impl!(self, 0)
+ }
+}
+
+impl<'context, A: RawLock + Sharable, B, B0, O> LockingTuple<'context, (A, B), (A, B0), O> {
+ /// Acquire a shared lock to the first element, and return a new tuple where
+ /// the first element is inaccessible.
+ #[must_use]
+ pub fn read_0<'a>(self) -> ReadReturn<'a, 'context, A, ((), B), (A, B0), O>
+ where
+ 'context: 'a,
+ {
+ read_impl!(self, 0)
+ }
+
+ /// Attempts to acquire a shared lock the first element without blocking, and
+ /// return a new tuple where the first element is inaccessible.
+ ///
+ /// # Errors
+ ///
+ /// If the element is already exclusively locked, `Err` is returned with the original
+ /// tuple.
+ pub fn try_read_0<'a>(self) -> TryReadReturn<'a, 'context, A, ((), B), (A, B0), O, Self>
+ where
+ 'context: 'a,
+ {
+ try_read_impl!(self, 0)
+ }
+
+ /// Acquire a shared lock to the first element. The tuple becomes unusable
+ /// until the returned guard is dropped.
+ #[must_use]
+ pub fn read_mut_0(&mut self) -> ContextGuard<'_, A::ReadGuard<'_>, ThreadKey> {
+ read_mut_impl!(self, 0)
+ }
+
+ /// Attempts to acquire a shared lock the first element without blocking. If
+ /// successful, the tuple becomes unusable until the returned guard is
+ /// dropped. If the element is already exclusively locked, `None` is returned.
+ #[must_use]
+ pub fn try_read_mut_0(&mut self) -> Option<ContextGuard<'_, A::ReadGuard<'_>, ThreadKey>> {
+ try_read_mut_impl!(self, 0)
+ }
+}
+
+impl<'context, A, B, B0, O> LockingTuple<'context, (A, B), (A, B0), O> {
+ /// Consume the tuple, and return the first element as a new tuple.
+ #[must_use]
+ pub fn recurse_0(self) -> RecurseReturn<'context, A, ((), B), (A, B0), O> {
+ recurse_impl!(self, 0)
+ }
+
+ /// Consume the tuple, and return the first element as a locking iterator.
+ pub fn recurse_0_iter(self) -> RecurseIterReturn<'context, A, ((), B), (A, B0), O>
+ where
+ &'context A: IntoIterator,
+ {
+ recurse_iter_impl!(self, 0)
+ }
+}
+
+impl<'context, A: Lockable + RawLock, B, O> LockingTuple<'context, (A, B), (A, B), O> {
+ /// Lock the first element, and return the second element as a new tuple.
+ #[must_use]
+ pub fn lock_and_recurse<'a>(
+ self,
+ ) -> (
+ ContextGuard<'a, <A as Lockable>::Guard<'a>, ThreadKey>,
+ LockingTuple<'context, B, B, O>,
+ )
+ where
+ 'context: 'a,
+ {
+ unsafe {
+ self.tuple.0.raw_write();
+ (
+ ContextGuard {
+ _key: self.key,
+ guard: self.tuple.0.guard(),
+ },
+ LockingTuple {
+ _lockable: PhantomData,
+ key: self.key,
+ tuple: &self.tuple.1,
+ outer: self.outer,
+ },
+ )
+ }
+ }
+}
+
+impl<'context, A, A0, B: RawLock + Lockable, O> LockingTuple<'context, (A, B), (A0, B), O> {
+ /// Lock the second element, and return a new tuple where the first and second
+ /// elements are inaccessible.
+ #[must_use]
+ pub fn lock_1<'a>(self) -> LockReturn<'a, 'context, B, ((), ()), (A0, B), O>
+ where
+ 'context: 'a,
+ {
+ lock_impl!(self, 1)
+ }
+
+ /// Lock the second element. The tuple becomes unusable until the returned
+ /// guard is dropped.
+ #[must_use]
+ pub fn lock_mut_1(&mut self) -> ContextGuard<'_, B::Guard<'_>, ThreadKey> {
+ lock_mut_impl!(self, 1)
+ }
+
+ /// Attempts to lock the second element without blocking, and return a new tuple
+ /// where the first element is inaccessible.
+ ///
+ /// # Errors
+ ///
+ /// If the element is already locked, `Err` is returned with the original
+ /// tuple.
+ pub fn try_lock_1<'a>(self) -> TryLockReturn<'a, 'context, B, ((), ()), (A0, B), O, Self>
+ where
+ 'context: 'a,
+ {
+ try_lock_impl!(self, 1)
+ }
+
+ /// Attempts to lock the second element without blocking. If successful, the
+ /// tuple becomes unusable until the returned guard is dropped. If the element
+ /// is already locked, `None` is returned.
+ #[must_use]
+ pub fn try_lock_mut_1(&mut self) -> Option<ContextGuard<'_, B::Guard<'_>, ThreadKey>> {
+ try_lock_mut_impl!(self, 1)
+ }
+}
+
+impl<'context, A, A0, B: RawLock + Sharable, O> LockingTuple<'context, (A, B), (A0, B), O> {
+ /// Acquire a shared lock to the second element, and return a new tuple where
+ /// the second element is inaccessible.
+ #[must_use]
+ pub fn read_1<'a>(self) -> ReadReturn<'a, 'context, B, ((), ()), (A0, B), O>
+ where
+ 'context: 'a,
+ {
+ read_impl!(self, 1)
+ }
+
+ /// Attempts to acquire a shared lock the second element without blocking, and
+ /// return a new tuple where the second element is inaccessible.
+ ///
+ /// # Errors
+ ///
+ /// If the element is already exclusively locked, `Err` is returned with the original
+ /// tuple.
+ pub fn try_read_1<'a>(self) -> TryReadReturn<'a, 'context, B, ((), ()), (A0, B), O, Self>
+ where
+ 'context: 'a,
+ {
+ try_read_impl!(self, 1)
+ }
+
+ /// Acquire a shared lock to the second element. The tuple becomes unusable
+ /// until the returned guard is dropped.
+ #[must_use]
+ pub fn read_mut_1(&mut self) -> ContextGuard<'_, B::ReadGuard<'_>, ThreadKey> {
+ read_mut_impl!(self, 1)
+ }
+
+ /// Attempts to acquire a shared lock the second element without blocking. If
+ /// successful, the tuple becomes unusable until the returned guard is
+ /// dropped. If the element is already exclusively locked, `None` is returned.
+ #[must_use]
+ pub fn try_read_mut_1(&mut self) -> Option<ContextGuard<'_, B::ReadGuard<'_>, ThreadKey>> {
+ try_read_mut_impl!(self, 1)
+ }
+}
+
+impl<'context, A, A0, B, O> LockingTuple<'context, (A, B), (A0, B), O> {
+ /// Consume the tuple, and return the second element as a new tuple.
+ #[must_use]
+ pub fn recurse_1(self) -> RecurseReturn<'context, B, ((), ()), (A0, B), O> {
+ recurse_impl!(self, 1)
+ }
+
+ /// Consume the tuple, and return the second element as a locking iterator.
+ pub fn recurse_1_iter(self) -> RecurseIterReturn<'context, B, (A, ()), (A0, B), O>
+ where
+ &'context B: IntoIterator,
+ {
+ recurse_iter_impl!(self, 1)
+ }
+}
+
+impl<'context, A: RawLock + Lockable, B, B0, C, C0, O>
+ LockingTuple<'context, (A, B, C), (A, B0, C0), O>
+{
+ /// Lock the first element, and return a new tuple where the first element
+ /// is inaccessible.
+ #[must_use]
+ // The type is impossible to refactor, and I already wrote this function, so no point in removing it
+ #[expect(clippy::type_complexity)]
+ pub fn lock_0<'a>(self) -> LockReturn<'a, 'context, A, ((), B, C), (A, B0, C0), O>
+ where
+ 'context: 'a,
+ {
+ lock_impl!(self, 0)
+ }
+
+ /// Lock the first element. The tuple becomes unusable until the returned
+ /// guard is dropped.
+ #[must_use]
+ pub fn lock_mut_0(&mut self) -> ContextGuard<'_, A::Guard<'_>, ThreadKey> {
+ lock_mut_impl!(self, 0)
+ }
+
+ /// Attempts to lock the first element without blocking. If successful, the
+ /// tuple becomes unusable until the returned guard is dropped. If the element
+ /// is already locked, `None` is returned.
+ #[must_use]
+ pub fn try_lock_mut_0(&mut self) -> Option<ContextGuard<'_, A::Guard<'_>, ThreadKey>> {
+ try_lock_mut_impl!(self, 0)
+ }
+}
+
+impl<'context, A: RawLock + Sharable, B, B0, C, C0, O>
+ LockingTuple<'context, (A, B, C), (A, B0, C0), O>
+{
+ /// Acquire a shared lock to the first element, and return a new tuple where
+ /// the first element is inaccessible.
+ #[must_use]
+ // The type is impossible to refactor, and I already wrote this function, so no point in removing it
+ #[expect(clippy::type_complexity)]
+ pub fn read_0<'a>(self) -> ReadReturn<'a, 'context, A, ((), B, C), (A, B0, C0), O>
+ where
+ 'context: 'a,
+ {
+ read_impl!(self, 0)
+ }
+
+ /// Acquire a shared lock to the first element. The tuple becomes unusable
+ /// until the returned guard is dropped.
+ #[must_use]
+ pub fn read_mut_0(&mut self) -> ContextGuard<'_, A::ReadGuard<'_>, ThreadKey> {
+ read_mut_impl!(self, 0)
+ }
+
+ /// Attempts to acquire a shared lock the first element without blocking. If
+ /// successful, the tuple becomes unusable until the returned guard is
+ /// dropped. If the element is already exclusively locked, `None` is returned.
+ #[must_use]
+ pub fn try_read_mut_0(&mut self) -> Option<ContextGuard<'_, A::ReadGuard<'_>, ThreadKey>> {
+ try_read_mut_impl!(self, 0)
+ }
+}
+
+impl<'context, A, B, B0, C, C0, O> LockingTuple<'context, (A, B, C), (A, B0, C0), O> {
+ /// Consume the tuple, and return the first element as a new tuple.
+ #[must_use]
+ // The type is impossible to refactor, and I already wrote this function, so no point in removing it
+ #[expect(clippy::type_complexity)]
+ pub fn recurse_0(self) -> RecurseReturn<'context, A, ((), B, C), (A, B0, C0), O> {
+ recurse_impl!(self, 0)
+ }
+
+ /// Consume the tuple, and return the first element as a locking iterator.
+ // The type is impossible to refactor, and I already wrote this function, so no point in removing it
+ #[expect(clippy::type_complexity)]
+ pub fn recurse_0_iter(self) -> RecurseIterReturn<'context, A, ((), B, C), (A, B0, C0), O>
+ where
+ &'context A: IntoIterator,
+ {
+ recurse_iter_impl!(self, 0)
+ }
+}
+
+impl<'context, A, A0, B: RawLock + Lockable, C, C0, O>
+ LockingTuple<'context, (A, B, C), (A0, B, C0), O>
+{
+ /// Lock the second element, and return a new tuple where the first and second
+ /// elements are inaccessible.
+ #[must_use]
+ // The type is impossible to refactor, and I already wrote this function, so no point in removing it
+ #[expect(clippy::type_complexity)]
+ pub fn lock_1<'a>(self) -> LockReturn<'a, 'context, B, ((), (), C), (A0, B, C0), O>
+ where
+ 'context: 'a,
+ {
+ lock_impl!(self, 1)
+ }
+
+ /// Lock the second element. The tuple becomes unusable until the returned
+ /// guard is dropped.
+ #[must_use]
+ pub fn lock_mut_1(&mut self) -> ContextGuard<'_, B::Guard<'_>, ThreadKey> {
+ lock_mut_impl!(self, 1)
+ }
+
+ /// Attempts to lock the second element without blocking. If successful, the
+ /// tuple becomes unusable until the returned guard is dropped. If the element
+ /// is already locked, `None` is returned.
+ #[must_use]
+ pub fn try_lock_mut_1(&mut self) -> Option<ContextGuard<'_, B::Guard<'_>, ThreadKey>> {
+ try_lock_mut_impl!(self, 1)
+ }
+}
+
+impl<'context, A, A0, B: RawLock + Sharable, C, C0, O>
+ LockingTuple<'context, (A, B, C), (A0, B, C0), O>
+{
+ /// Acquire a shared lock to the second element, and return a new tuple where
+ /// the second element is inaccessible.
+ #[must_use]
+ // The type is impossible to refactor, and I already wrote this function, so no point in removing it
+ #[expect(clippy::type_complexity)]
+ pub fn read_1<'a>(self) -> ReadReturn<'a, 'context, B, ((), (), C), (A0, B, C0), O>
+ where
+ 'context: 'a,
+ {
+ read_impl!(self, 1)
+ }
+
+ /// Acquire a shared lock to the second element. The tuple becomes unusable
+ /// until the returned guard is dropped.
+ #[must_use]
+ pub fn read_mut_1(&mut self) -> ContextGuard<'_, B::ReadGuard<'_>, ThreadKey> {
+ read_mut_impl!(self, 1)
+ }
+
+ /// Attempts to acquire a shared lock the second element without blocking. If
+ /// successful, the tuple becomes unusable until the returned guard is
+ /// dropped. If the element is already exclusively locked, `None` is returned.
+ #[must_use]
+ pub fn try_read_mut_1(&mut self) -> Option<ContextGuard<'_, B::ReadGuard<'_>, ThreadKey>> {
+ try_read_mut_impl!(self, 1)
+ }
+}
+
+impl<'context, A, A0, B, C, C0, O> LockingTuple<'context, (A, B, C), (A0, B, C0), O> {
+ /// Consume the tuple, and return the second element as a new tuple.
+ #[must_use]
+ // The type is impossible to refactor, and I already wrote this function, so no point in removing it
+ #[expect(clippy::type_complexity)]
+ pub fn recurse_1(self) -> RecurseReturn<'context, B, ((), (), C), (A0, B, C0), O> {
+ recurse_impl!(self, 1)
+ }
+
+ /// Consume the tuple, and return the second element as a locking iterator.
+ // The type is impossible to refactor, and I already wrote this function, so no point in removing it
+ #[expect(clippy::type_complexity)]
+ pub fn recurse_1_iter(self) -> RecurseIterReturn<'context, B, ((), (), C), (A0, B, C0), O>
+ where
+ &'context B: IntoIterator,
+ {
+ recurse_iter_impl!(self, 1)
+ }
+}
+
+impl<'context, A, A0, B, B0, C: RawLock + Lockable, O>
+ LockingTuple<'context, (A, B, C), (A0, B0, C), O>
+{
+ /// Lock the third element, and return a new tuple where all elements are
+ /// inaccessible.
+ #[must_use]
+ // The type is impossible to refactor, and I already wrote this function, so no point in removing it
+ #[expect(clippy::type_complexity)]
+ pub fn lock_2<'a>(self) -> LockReturn<'a, 'context, C, ((), (), ()), (A0, B0, C), O>
+ where
+ 'context: 'a,
+ {
+ lock_impl!(self, 2)
+ }
+
+ /// Lock the third element. The tuple becomes unusable until the returned
+ /// guard is dropped.
+ #[must_use]
+ pub fn lock_mut_2(&mut self) -> ContextGuard<'_, C::Guard<'_>, ThreadKey> {
+ lock_mut_impl!(self, 2)
+ }
+
+ /// Attempts to lock the third element without blocking. If successful, the
+ /// tuple becomes unusable until the returned guard is dropped. If the element
+ /// is already locked, `None` is returned.
+ #[must_use]
+ pub fn try_lock_mut_2(&mut self) -> Option<ContextGuard<'_, C::Guard<'_>, ThreadKey>> {
+ try_lock_mut_impl!(self, 2)
+ }
+}
+
+impl<'context, A, A0, B, B0, C: RawLock + Sharable, O>
+ LockingTuple<'context, (A, B, C), (A0, B0, C), O>
+{
+ /// Acquire a shared lock to the third element, and return a new tuple where
+ /// the third element is inaccessible.
+ #[must_use]
+ // The type is impossible to refactor, and I already wrote this function, so no point in removing it
+ #[expect(clippy::type_complexity)]
+ pub fn read_2<'a>(self) -> ReadReturn<'a, 'context, C, ((), (), ()), (A0, B0, C), O>
+ where
+ 'context: 'a,
+ {
+ read_impl!(self, 2)
+ }
+
+ /// Acquire a shared lock to the third element. The tuple becomes unusable
+ /// until the returned guard is dropped.
+ #[must_use]
+ pub fn read_mut_2(&mut self) -> ContextGuard<'_, C::ReadGuard<'_>, ThreadKey> {
+ read_mut_impl!(self, 2)
+ }
+
+ /// Attempts to acquire a shared lock the third element without blocking. If
+ /// successful, the tuple becomes unusable until the returned guard is
+ /// dropped. If the element is already exclusively locked, `None` is returned.
+ #[must_use]
+ pub fn try_read_mut_2(&mut self) -> Option<ContextGuard<'_, C::ReadGuard<'_>, ThreadKey>> {
+ try_read_mut_impl!(self, 2)
+ }
+}
+
+impl<'context, A, A0, B, B0, C, O> LockingTuple<'context, (A, B, C), (A0, B0, C), O> {
+ /// Consume the tuple, and return the third element as a new tuple.
+ #[must_use]
+ // The type is impossible to refactor, and I already wrote this function, so no point in removing it
+ #[expect(clippy::type_complexity)]
+ pub fn recurse_2(self) -> RecurseReturn<'context, C, ((), (), ()), (A0, B0, C), O> {
+ recurse_impl!(self, 2)
+ }
+
+ /// Consume the tuple, and return the third element as a locking iterator.
+ // The type is impossible to refactor, and I already wrote this function, so no point in removing it
+ #[expect(clippy::type_complexity)]
+ pub fn recurse_2_iter(self) -> RecurseIterReturn<'context, C, ((), (), ()), (A0, B0, C), O>
+ where
+ &'context C: IntoIterator,
+ {
+ recurse_iter_impl!(self, 2)
+ }
+}
+
+impl<'context, A, B, B0, C, C0, D, D0, O> LockingTuple<'context, (A, B, C, D), (A, B0, C0, D0), O> {
+ /// Consume the tuple, and return the first element as a new tuple.
+ #[must_use]
+ // The type is impossible to refactor, and I already wrote this function, so no point in removing it
+ #[expect(clippy::type_complexity)]
+ pub fn recurse_0(self) -> RecurseReturn<'context, A, ((), B, C, D), (A, B0, C0, D0), O> {
+ recurse_impl!(self, 0)
+ }
+
+ /// Consume the tuple, and return the first element as a locking iterator.
+ // The type is impossible to refactor, and I already wrote this function, so no point in removing it
+ #[expect(clippy::type_complexity)]
+ pub fn recurse_0_iter(self) -> RecurseIterReturn<'context, A, ((), B, C, D), (A, B0, C0, D0), O>
+ where
+ &'context A: IntoIterator,
+ {
+ recurse_iter_impl!(self, 0)
+ }
+}
+
+impl<'context, A, A0, B, C, C0, D, D0, O> LockingTuple<'context, (A, B, C, D), (A0, B, C0, D0), O> {
+ /// Consume the tuple, and return the second element as a new tuple.
+ #[must_use]
+ // The type is impossible to refactor, and I already wrote this function, so no point in removing it
+ #[expect(clippy::type_complexity)]
+ pub fn recurse_1(self) -> RecurseReturn<'context, B, ((), (), C, D), (A0, B, C0, D0), O> {
+ recurse_impl!(self, 1)
+ }
+
+ /// Consume the tuple, and return the second element as a locking iterator.
+ // The type is impossible to refactor, and I already wrote this function, so no point in removing it
+ #[expect(clippy::type_complexity)]
+ pub fn recurse_1_iter(
+ self,
+ ) -> RecurseIterReturn<'context, B, ((), (), C, D), (A0, B, C0, D0), O>
+ where
+ &'context B: IntoIterator,
+ {
+ recurse_iter_impl!(self, 1)
+ }
+}
+
+impl<'context, A, A0, B, B0, C, D, D0, O> LockingTuple<'context, (A, B, C, D), (A0, B0, C, D0), O> {
+ /// Consume the tuple, and return the third element as a new tuple.
+ #[must_use]
+ // The type is impossible to refactor, and I already wrote this function, so no point in removing it
+ #[expect(clippy::type_complexity)]
+ pub fn recurse_2(self) -> RecurseReturn<'context, C, ((), (), (), D), (A0, B0, C, D0), O> {
+ recurse_impl!(self, 2)
+ }
+
+ /// Consume the tuple, and return the third element as a locking iterator.
+ // The type is impossible to refactor, and I already wrote this function, so no point in removing it
+ #[expect(clippy::type_complexity)]
+ pub fn recurse_2_iter(
+ self,
+ ) -> RecurseIterReturn<'context, C, ((), (), (), D), (A0, B0, C, D0), O>
+ where
+ &'context C: IntoIterator,
+ {
+ recurse_iter_impl!(self, 2)
+ }
+}
+
+impl<'context, A, A0, B, B0, C, C0, D, O> LockingTuple<'context, (A, B, C, D), (A0, B0, C0, D), O> {
+ /// Consume the tuple, and return the fourth element as a new tuple.
+ #[must_use]
+ // The type is impossible to refactor, and I already wrote this function, so no point in removing it
+ #[expect(clippy::type_complexity)]
+ pub fn recurse_3(self) -> RecurseReturn<'context, D, ((), (), (), ()), (A0, B0, C0, D), O> {
+ recurse_impl!(self, 3)
+ }
+
+ /// Consume the tuple, and return the first element as a locking iterator.
+ // The type is impossible to refactor, and I already wrote this function, so no point in removing it
+ #[expect(clippy::type_complexity)]
+ pub fn recurse_3_iter(
+ self,
+ ) -> RecurseIterReturn<'context, D, ((), (), (), ()), (A0, B0, C0, D), O>
+ where
+ &'context D: IntoIterator,
+ {
+ recurse_iter_impl!(self, 3)
+ }
+}