From 6f6e030ea7edb9d155ebf21b5d42936c20801b50 Mon Sep 17 00:00:00 2001 From: Mica White Date: Sun, 16 Aug 2026 12:43:05 -0400 Subject: Implement LockContext --- src/collection/owned.rs | 27 +----- src/iterator.rs | 29 ++++-- src/iterator/consumed_guard.rs | 58 ------------ src/iterator/context.rs | 52 +++++++++++ src/iterator/iterator.rs | 81 +++++++++-------- src/iterator/tuple.rs | 202 +++++++++++++++++++++++++++++++++++++++++ src/lockable.rs | 7 ++ 7 files changed, 331 insertions(+), 125 deletions(-) delete mode 100644 src/iterator/consumed_guard.rs create mode 100644 src/iterator/context.rs create mode 100644 src/iterator/tuple.rs diff --git a/src/collection/owned.rs b/src/collection/owned.rs index a7ab1b6..07a5402 100755 --- a/src/collection/owned.rs +++ b/src/collection/owned.rs @@ -1,4 +1,4 @@ -use crate::iterator::LockingIterator; +use crate::iterator::LockContext; use crate::lockable::{ Lockable, LockableGetMut, LockableIntoInner, OwnedLockable, RawLock, Sharable, }; @@ -559,6 +559,10 @@ impl OwnedLockCollection { } impl OwnedLockCollection { + pub const fn context(&self) -> LockContext<'_, L> { + LockContext::new(&self.child) + } + /// Gets the underlying collection, consuming this collection. /// /// # Examples @@ -641,27 +645,6 @@ impl OwnedLockCollection { } } -impl OwnedLockCollection -where - for<'a> &'a L: IntoIterator, -{ - 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( - &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)] mod tests { use super::*; diff --git a/src/iterator.rs b/src/iterator.rs index 30b01e0..79a2cc5 100644 --- a/src/iterator.rs +++ b/src/iterator.rs @@ -1,18 +1,29 @@ -mod consumed_guard; +use std::marker::PhantomData; + +use crate::ThreadKey; + +mod context; mod guard; mod iterator; +mod tuple; -pub struct LockingIterator { - key: Key, - lockable: L, +pub struct LockingIterator<'context, I> { + key: &'context ThreadKey, + iterator: I, } -pub struct IteratorGuard<'a, Guard, Key> { - _key: &'a Key, - guard: Guard, +pub struct LockingTuple<'context, L, C> { + _lockable: PhantomData, + key: &'context ThreadKey, + tuple: &'context C, } -pub struct ConsumedIteratorGuard { - key: Key, +pub struct LockContext<'l, L> { + key: Option, + lockable: &'l L, +} + +pub struct IteratorGuard<'a, Guard, Key> { + _key: &'a Key, guard: Guard, } diff --git a/src/iterator/consumed_guard.rs b/src/iterator/consumed_guard.rs deleted file mode 100644 index 5331289..0000000 --- a/src/iterator/consumed_guard.rs +++ /dev/null @@ -1,58 +0,0 @@ -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 Hash for ConsumedIteratorGuard { - fn hash(&self, state: &mut H) { - self.guard.hash(state) - } -} - -// No implementations of Eq, PartialEq, PartialOrd, or Ord -// You can't implement both PartialEq and PartialEq -// 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 Debug for ConsumedIteratorGuard { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - Debug::fmt(&**self, f) - } -} - -impl Display for ConsumedIteratorGuard { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - Display::fmt(&**self, f) - } -} - -impl Deref for ConsumedIteratorGuard { - type Target = Guard; - - fn deref(&self) -> &Self::Target { - &self.guard - } -} - -impl DerefMut for ConsumedIteratorGuard { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.guard - } -} - -impl AsRef for ConsumedIteratorGuard { - fn as_ref(&self) -> &Guard { - &self.guard - } -} - -impl AsMut for ConsumedIteratorGuard { - fn as_mut(&mut self) -> &mut Guard { - &mut self.guard - } -} diff --git a/src/iterator/context.rs b/src/iterator/context.rs new file mode 100644 index 0000000..a5c9123 --- /dev/null +++ b/src/iterator/context.rs @@ -0,0 +1,52 @@ +use std::marker::PhantomData; + +use crate::{ + iterator::{LockContext, LockingIterator, LockingTuple}, + lockable::Lockable, + ThreadKey, +}; + +impl<'l, L> LockContext<'l, L> { + pub(crate) const fn new(lockable: &'l L) -> Self { + Self { + key: None, + lockable, + } + } +} + +impl LockContext<'_, L> { + 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, + } + } + } +} + +impl<'l, L> LockContext<'l, L> +where + &'l L: IntoIterator, +{ + #[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(), + } + } + } +} diff --git a/src/iterator/iterator.rs b/src/iterator/iterator.rs index 1e389a4..316a276 100644 --- a/src/iterator/iterator.rs +++ b/src/iterator/iterator.rs @@ -1,36 +1,32 @@ use std::iter::{Enumerate, Fuse, Skip, Take}; -use super::{ConsumedIteratorGuard, IteratorGuard, LockingIterator}; +use super::{IteratorGuard, LockingIterator}; use crate::{ lockable::{Lockable, RawLock, Sharable}, - Keyable, + ThreadKey, }; -impl LockingIterator { - pub(crate) const fn new(key: Key, lockable: L) -> Self { - Self { key, lockable } - } - - fn with_iterator(self, f: impl FnOnce(L) -> M) -> LockingIterator { +impl<'l, I> LockingIterator<'l, I> { + fn with_iterator(self, f: impl FnOnce(I) -> M) -> LockingIterator<'l, M> { LockingIterator { key: self.key, - lockable: f(self.lockable), + iterator: f(self.iterator), } } } -impl<'a, I: Iterator, L: 'a + Lockable + RawLock, Key: Keyable> - LockingIterator -{ - pub fn lock_next(&'a mut self) -> Option::Guard<'a>, Key>> { - if let Some(lock) = self.lockable.next() { +impl<'c, L: 'c + Iterator, I: 'c + RawLock + Lockable> LockingIterator<'c, L> { + pub fn lock_next( + &mut self, + ) -> Option::Guard<'c>, ThreadKey>> { + if let Some(lock) = self.iterator.next() { unsafe { lock.raw_write(); let guard = lock.guard(); Some(IteratorGuard { - _key: &self.key, + _key: self.key, guard, }) } @@ -39,32 +35,30 @@ impl<'a, I: Iterator, L: 'a + Lockable + RawLock, Key: Keyable> } } - pub fn lock_last(self) -> Option::Guard<'a>, Key>> { - self.lockable.last().map(|lock| unsafe { + pub fn lock_last(self) -> Option::Guard<'c>, ThreadKey>> { + self.iterator.last().map(|lock| unsafe { lock.raw_write(); let guard = lock.guard(); - ConsumedIteratorGuard { - key: self.key, + IteratorGuard { + _key: self.key, guard, } }) } } -impl<'a, I: Iterator, L: 'a + Sharable + RawLock, Key: Keyable> - LockingIterator -{ +impl<'c, L: 'c + Iterator, I: 'c + RawLock + Sharable> LockingIterator<'c, L> { pub fn read_next( - &'a mut self, - ) -> Option::ReadGuard<'a>, Key>> { - if let Some(lock) = self.lockable.next() { + &mut self, + ) -> Option::ReadGuard<'c>, ThreadKey>> { + if let Some(lock) = self.iterator.next() { unsafe { lock.raw_read(); let guard = lock.read_guard(); Some(IteratorGuard { - _key: &self.key, + _key: self.key, guard, }) } @@ -73,37 +67,52 @@ impl<'a, I: Iterator, L: 'a + Sharable + RawLock, Key: Keyable> } } - pub fn read_last(self) -> Option::ReadGuard<'a>, Key>> { - self.lockable.last().map(|lock| unsafe { + pub fn read_last(self) -> Option::ReadGuard<'c>, ThreadKey>> { + self.iterator.last().map(|lock| unsafe { lock.raw_read(); let guard = lock.read_guard(); - ConsumedIteratorGuard { - key: self.key, + IteratorGuard { + _key: self.key, guard, } }) } } -impl LockingIterator { +impl<'l, L: Iterator> LockingIterator<'l, L> { + pub fn skip_next(&mut self) -> Option { + self.iterator.next() + } + + pub fn skip_mut(&mut self, n: usize) { + for _ in 0..n { + self.iterator.next(); + } + } + + #[must_use] pub fn size_hint(&self) -> (usize, Option) { - self.lockable.size_hint() + self.iterator.size_hint() } - pub fn enumerate(self) -> LockingIterator, Key> { + #[must_use] + pub fn enumerate(self) -> LockingIterator<'l, Enumerate> { self.with_iterator(Iterator::enumerate) } - pub fn skip(self, n: usize) -> LockingIterator, Key> { + #[must_use] + pub fn skip(self, n: usize) -> LockingIterator<'l, Skip> { self.with_iterator(|i| i.skip(n)) } - pub fn take(self, n: usize) -> LockingIterator, Key> { + #[must_use] + pub fn take(self, n: usize) -> LockingIterator<'l, Take> { self.with_iterator(|i| i.take(n)) } - pub fn fuse(self) -> LockingIterator, Key> { + #[must_use] + pub fn fuse(self) -> LockingIterator<'l, Fuse> { self.with_iterator(Iterator::fuse) } } diff --git a/src/iterator/tuple.rs b/src/iterator/tuple.rs new file mode 100644 index 0000000..855cdc7 --- /dev/null +++ b/src/iterator/tuple.rs @@ -0,0 +1,202 @@ +use std::marker::PhantomData; + +use crate::{ + iterator::{IteratorGuard, LockingTuple}, + lockable::{Lockable, RawLock}, + ThreadKey, +}; + +impl<'c, A, B> LockingTuple<'c, A, B> { + const fn transmute(self) -> LockingTuple<'c, C, B> { + LockingTuple { + _lockable: PhantomData, + key: self.key, + tuple: self.tuple, + } + } +} + +macro_rules! lock_impl { + ($self: expr, $field: tt) => { + unsafe { + $self.tuple.$field.raw_write(); + ( + IteratorGuard { + _key: &$self.key, + guard: $self.tuple.$field.guard(), + }, + $self.transmute(), + ) + } + }; +} + +macro_rules! recurse_impl { + ($self: expr, $field: tt) => { + LockingTuple { + _lockable: PhantomData, + key: $self.key, + tuple: &$self.tuple.$field, + } + }; +} + +type LockReturn<'a, 'context, Guarded, L, C> = ( + IteratorGuard<'a, ::Guard<'a>, ThreadKey>, + LockingTuple<'context, L, C>, +); + +impl<'context, A: RawLock + Lockable> LockingTuple<'context, (A,), (A,)> { + #[must_use] + pub fn lock_0<'a>(self) -> LockReturn<'a, 'context, A, ((),), (A,)> + where + 'context: 'a, + { + lock_impl!(self, 0) + } +} + +impl<'context, A> LockingTuple<'context, (A,), (A,)> { + #[must_use] + pub const fn recurse_0(self) -> LockingTuple<'context, A, A> { + recurse_impl!(self, 0) + } +} + +impl<'context, A: RawLock + Lockable, B, B0> LockingTuple<'context, (A, B), (A, B0)> { + #[must_use] + pub fn lock_0<'a>(self) -> LockReturn<'a, 'context, A, ((), B), (A, B0)> + where + 'context: 'a, + { + lock_impl!(self, 0) + } +} + +impl<'context, A, B, B0> LockingTuple<'context, (A, B), (A, B0)> { + #[must_use] + pub const fn recurse_0(self) -> LockingTuple<'context, A, A> { + recurse_impl!(self, 0) + } +} + +impl<'context, A: Lockable + RawLock, B> LockingTuple<'context, (A, B), (A, B)> { + #[must_use] + pub fn lock_and_recurse<'a>( + self, + ) -> ( + IteratorGuard<'a, ::Guard<'a>, ThreadKey>, + LockingTuple<'context, B, B>, + ) + where + 'context: 'a, + { + unsafe { + self.tuple.0.raw_write(); + ( + IteratorGuard { + _key: self.key, + guard: self.tuple.0.guard(), + }, + LockingTuple { + _lockable: PhantomData, + key: self.key, + tuple: &self.tuple.1, + }, + ) + } + } +} + +impl<'context, A, A0, B: RawLock + Lockable> LockingTuple<'context, (A, B), (A0, B)> { + #[must_use] + pub fn lock_1<'a>(self) -> LockReturn<'a, 'context, B, ((), ()), (A0, B)> + where + 'context: 'a, + { + lock_impl!(self, 1) + } +} + +impl<'context, A, A0, B> LockingTuple<'context, (A, B), (A0, B)> { + #[must_use] + pub const fn recurse_1(self) -> LockingTuple<'context, B, B> { + recurse_impl!(self, 1) + } +} + +impl<'context, A: RawLock + Lockable, B, B0, C, C0> LockingTuple<'context, (A, B, C), (A, B0, C0)> { + #[must_use] + pub fn lock_0<'a>(self) -> LockReturn<'a, 'context, A, ((), B, C), (A, B0, C0)> + where + 'context: 'a, + { + lock_impl!(self, 0) + } +} + +impl<'context, A, B, B0, C, C0> LockingTuple<'context, (A, B, C), (A, B0, C0)> { + #[must_use] + pub const fn recurse_0(self) -> LockingTuple<'context, A, A> { + recurse_impl!(self, 0) + } +} + +impl<'context, A, A0, B: RawLock + Lockable, C, C0> LockingTuple<'context, (A, B, C), (A0, B, C0)> { + #[must_use] + pub fn lock_1<'a>(self) -> LockReturn<'a, 'context, B, ((), (), C), (A0, B, C0)> + where + 'context: 'a, + { + lock_impl!(self, 1) + } +} + +impl<'context, A, A0, B, C, C0> LockingTuple<'context, (A, B, C), (A0, B, C0)> { + #[must_use] + pub const fn recurse_1(self) -> LockingTuple<'context, B, B> { + recurse_impl!(self, 1) + } +} + +impl<'context, A, A0, B, B0, C: RawLock + Lockable> LockingTuple<'context, (A, B, C), (A0, B0, C)> { + #[must_use] + pub fn lock_2<'a>(self) -> LockReturn<'a, 'context, C, ((), (), ()), (A0, B0, C)> + where + 'context: 'a, + { + lock_impl!(self, 2) + } +} + +impl<'context, A, A0, B, B0, C> LockingTuple<'context, (A, B, C), (A0, B0, C)> { + #[must_use] + pub const fn recurse_2(self) -> LockingTuple<'context, C, C> { + recurse_impl!(self, 2) + } +} + +impl<'context, A, B, B0, C, C0, D, D0> LockingTuple<'context, (A, B, C, D), (A, B0, C0, D0)> { + #[must_use] + pub const fn recurse_0(self) -> LockingTuple<'context, A, A> { + recurse_impl!(self, 0) + } +} +impl<'context, A, A0, B, C, C0, D, D0> LockingTuple<'context, (A, B, C, D), (A0, B, C0, D0)> { + #[must_use] + pub const fn recurse_1(self) -> LockingTuple<'context, B, B> { + recurse_impl!(self, 1) + } +} +impl<'context, A, A0, B, B0, C, D, D0> LockingTuple<'context, (A, B, C, D), (A0, B0, C, D0)> { + #[must_use] + pub const fn recurse_2(self) -> LockingTuple<'context, C, C> { + recurse_impl!(self, 2) + } +} +impl<'context, A, A0, B, B0, C, C0, D> LockingTuple<'context, (A, B, C, D), (A0, B0, C0, D)> { + #[must_use] + pub const fn recurse_3(self) -> LockingTuple<'context, D, D> { + recurse_impl!(self, 3) + } +} diff --git a/src/lockable.rs b/src/lockable.rs index 56cbb52..badbe8e 100755 --- a/src/lockable.rs +++ b/src/lockable.rs @@ -401,6 +401,13 @@ tuple_impls!(A B C D, 0 1 2 3); tuple_impls!(A B C D E, 0 1 2 3 4); tuple_impls!(A B C D E F, 0 1 2 3 4 5); tuple_impls!(A B C D E F G, 0 1 2 3 4 5 6); +tuple_impls!(A B C D E F G H, 0 1 2 3 4 5 6 7); +tuple_impls!(A B C D E F G H I, 0 1 2 3 4 5 6 7 8); +tuple_impls!(A B C D E F G H I J, 0 1 2 3 4 5 6 7 8 9); +tuple_impls!(A B C D E F G H I J K, 0 1 2 3 4 5 6 7 8 9 10); +tuple_impls!(A B C D E F G H I J K L, 0 1 2 3 4 5 6 7 8 9 10 11); +tuple_impls!(A B C D E F G H I J K L M, 0 1 2 3 4 5 6 7 8 9 10 11 12); +tuple_impls!(A B C D E F G H I J K L M N, 0 1 2 3 4 5 6 7 8 9 10 11 12 13); unsafe impl Lockable for [T; N] { type Guard<'g> -- cgit v1.3.1