From 37ab873d21ca1fcd43db8d6a26d5bac4f5285f71 Mon Sep 17 00:00:00 2001 From: Mica White Date: Wed, 25 Dec 2024 17:58:06 -0500 Subject: Move some logic into the Sharable trait --- src/lockable.rs | 309 +++++++++++++++++++++----------------------------------- 1 file changed, 114 insertions(+), 195 deletions(-) (limited to 'src/lockable.rs') diff --git a/src/lockable.rs b/src/lockable.rs index 78f008e..5cc7135 100644 --- a/src/lockable.rs +++ b/src/lockable.rs @@ -1,9 +1,5 @@ use std::mem::MaybeUninit; -use crate::rwlock::{ReadLock, RwLockReadRef, RwLockWriteRef, WriteLock}; - -use lock_api::RawRwLock; - /// A raw lock type that may be locked and unlocked /// /// # Safety @@ -116,11 +112,6 @@ pub unsafe trait Lockable { where Self: 'g; - /// The shared guard type that does not hold a key - type ReadGuard<'g> - where - Self: 'g; - /// Yields a list of references to the [`RawLock`]s contained within this /// value. /// @@ -139,17 +130,6 @@ pub unsafe trait Lockable { /// unlocked until this guard is dropped. #[must_use] unsafe fn guard(&self) -> Self::Guard<'_>; - - /// Returns a guard that can be used to immutably access the underlying - /// data. - /// - /// # Safety - /// - /// All locks given by calling [`Lockable::get_ptrs`] must be locked using - /// [`RawLock::read`] before calling this function. The locks must not be - /// unlocked until this guard is dropped. - #[must_use] - unsafe fn read_guard(&self) -> Self::ReadGuard<'_>; } /// A trait which indicates that `into_inner` is a valid operation for a @@ -190,14 +170,24 @@ pub trait LockableAsMut: Lockable { fn as_mut(&mut self) -> Self::Inner<'_>; } -/// A marker trait to indicate that multiple readers can access the lock at a -/// time. -/// -/// # Safety -/// -/// This type must only be implemented if the lock can be safely shared between -/// multiple readers. -pub unsafe trait Sharable: Lockable {} +/// Allows a lock to be accessed by multiple readers. +pub unsafe trait Sharable: Lockable { + /// The shared guard type that does not hold a key + type ReadGuard<'g> + where + Self: 'g; + + /// Returns a guard that can be used to immutably access the underlying + /// data. + /// + /// # Safety + /// + /// All locks given by calling [`Lockable::get_ptrs`] must be locked using + /// [`RawLock::read`] before calling this function. The locks must not be + /// unlocked until this guard is dropped. + #[must_use] + unsafe fn read_guard(&self) -> Self::ReadGuard<'_>; +} /// A type that may be locked and unlocked, and is known to be the only valid /// instance of the lock. @@ -208,98 +198,38 @@ pub unsafe trait Sharable: Lockable {} /// time, i.e., this must either be an owned value or a mutable reference. pub unsafe trait OwnedLockable: Lockable {} -unsafe impl Lockable for ReadLock<'_, T, R> { - type Guard<'g> - = RwLockReadRef<'g, T, R> - where - Self: 'g; - - type ReadGuard<'g> - = RwLockReadRef<'g, T, R> - where - Self: 'g; - - fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { - ptrs.push(self.as_ref()); - } - - unsafe fn guard(&self) -> Self::Guard<'_> { - RwLockReadRef::new(self.as_ref()) - } - - unsafe fn read_guard(&self) -> Self::Guard<'_> { - RwLockReadRef::new(self.as_ref()) - } -} - -unsafe impl Lockable for WriteLock<'_, T, R> { +unsafe impl Lockable for &T { type Guard<'g> - = RwLockWriteRef<'g, T, R> - where - Self: 'g; - - type ReadGuard<'g> - = RwLockWriteRef<'g, T, R> + = T::Guard<'g> where Self: 'g; fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { - ptrs.push(self.as_ref()); + (*self).get_ptrs(ptrs); } unsafe fn guard(&self) -> Self::Guard<'_> { - RwLockWriteRef::new(self.as_ref()) - } - - unsafe fn read_guard(&self) -> Self::Guard<'_> { - RwLockWriteRef::new(self.as_ref()) + (*self).guard() } } -// Technically, the exclusive locks can also be shared, but there's currently -// no way to express that. I don't think I want to ever express that. -unsafe impl Sharable for ReadLock<'_, T, R> {} - -// Because both ReadLock and WriteLock hold references to RwLocks, they can't -// implement OwnedLockable - -unsafe impl Lockable for &T { - type Guard<'g> - = T::Guard<'g> - where - Self: 'g; - +unsafe impl Sharable for &T { type ReadGuard<'g> = T::ReadGuard<'g> where Self: 'g; - fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { - (*self).get_ptrs(ptrs); - } - - unsafe fn guard(&self) -> Self::Guard<'_> { - (*self).guard() - } - unsafe fn read_guard(&self) -> Self::ReadGuard<'_> { (*self).read_guard() } } -unsafe impl Sharable for &T {} - unsafe impl Lockable for &mut T { type Guard<'g> = T::Guard<'g> where Self: 'g; - type ReadGuard<'g> - = T::ReadGuard<'g> - where - Self: 'g; - fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { (**self).get_ptrs(ptrs) } @@ -307,10 +237,6 @@ unsafe impl Lockable for &mut T { unsafe fn guard(&self) -> Self::Guard<'_> { (**self).guard() } - - unsafe fn read_guard(&self) -> Self::ReadGuard<'_> { - (**self).read_guard() - } } impl LockableAsMut for &mut T { @@ -324,7 +250,16 @@ impl LockableAsMut for &mut T { } } -unsafe impl Sharable for &mut T {} +unsafe impl Sharable for &mut T { + type ReadGuard<'g> + = T::ReadGuard<'g> + where + Self: 'g; + + unsafe fn read_guard(&self) -> Self::ReadGuard<'_> { + (**self).read_guard() + } +} unsafe impl OwnedLockable for &mut T {} @@ -335,7 +270,7 @@ macro_rules! tuple_impls { unsafe impl<$($generic: Lockable,)*> Lockable for ($($generic,)*) { type Guard<'g> = ($($generic::Guard<'g>,)*) where Self: 'g; - type ReadGuard<'g> = ($($generic::ReadGuard<'g>,)*) where Self: 'g; + fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { self.0.get_ptrs(ptrs); @@ -346,10 +281,6 @@ macro_rules! tuple_impls { // I don't think any other way of doing it compiles ($(self.$value.guard(),)*) } - - unsafe fn read_guard(&self) -> Self::ReadGuard<'_> { - ($(self.$value.read_guard(),)*) - } } impl<$($generic: LockableAsMut,)*> LockableAsMut for ($($generic,)*) { @@ -368,7 +299,13 @@ macro_rules! tuple_impls { } } - unsafe impl<$($generic: Sharable,)*> Sharable for ($($generic,)*) {} + unsafe impl<$($generic: Sharable,)*> Sharable for ($($generic,)*) { + type ReadGuard<'g> = ($($generic::ReadGuard<'g>,)*) where Self: 'g; + + unsafe fn read_guard(&self) -> Self::ReadGuard<'_> { + ($(self.$value.read_guard(),)*) + } + } unsafe impl<$($generic: OwnedLockable,)*> OwnedLockable for ($($generic,)*) {} }; @@ -388,11 +325,6 @@ unsafe impl Lockable for [T; N] { where Self: 'g; - type ReadGuard<'g> - = [T::ReadGuard<'g>; N] - where - Self: 'g; - fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { for lock in self { lock.get_ptrs(ptrs); @@ -409,6 +341,46 @@ unsafe impl Lockable for [T; N] { guards.map(|g| g.assume_init()) } +} + +impl LockableAsMut for [T; N] { + type Inner<'a> + = [T::Inner<'a>; N] + where + Self: 'a; + + fn as_mut(&mut self) -> Self::Inner<'_> { + unsafe { + let mut guards = MaybeUninit::<[MaybeUninit>; N]>::uninit().assume_init(); + for (i, lock) in self.iter_mut().enumerate() { + guards[i].write(lock.as_mut()); + } + + guards.map(|g| g.assume_init()) + } + } +} + +impl LockableIntoInner for [T; N] { + type Inner = [T::Inner; N]; + + fn into_inner(self) -> Self::Inner { + unsafe { + let mut guards = MaybeUninit::<[MaybeUninit; N]>::uninit().assume_init(); + for (i, lock) in self.into_iter().enumerate() { + guards[i].write(lock.into_inner()); + } + + guards.map(|g| g.assume_init()) + } + } +} + +unsafe impl Sharable for [T; N] { + type ReadGuard<'g> + = [T::ReadGuard<'g>; N] + where + Self: 'g; unsafe fn read_guard<'g>(&'g self) -> Self::ReadGuard<'g> { let mut guards = MaybeUninit::<[MaybeUninit>; N]>::uninit().assume_init(); @@ -420,17 +392,14 @@ unsafe impl Lockable for [T; N] { } } +unsafe impl OwnedLockable for [T; N] {} + unsafe impl Lockable for Box<[T]> { type Guard<'g> = Box<[T::Guard<'g>]> where Self: 'g; - type ReadGuard<'g> - = Box<[T::ReadGuard<'g>]> - where - Self: 'g; - fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { for lock in self.iter() { lock.get_ptrs(ptrs); @@ -440,86 +409,64 @@ unsafe impl Lockable for Box<[T]> { unsafe fn guard(&self) -> Self::Guard<'_> { self.iter().map(|lock| lock.guard()).collect() } - - unsafe fn read_guard(&self) -> Self::ReadGuard<'_> { - self.iter().map(|lock| lock.read_guard()).collect() - } } -unsafe impl Lockable for Vec { - // There's no reason why I'd ever want to extend a list of lock guards - type Guard<'g> - = Box<[T::Guard<'g>]> +impl LockableAsMut for Box<[T]> { + type Inner<'a> + = Box<[T::Inner<'a>]> where - Self: 'g; + Self: 'a; + + fn as_mut(&mut self) -> Self::Inner<'_> { + self.iter_mut().map(LockableAsMut::as_mut).collect() + } +} +unsafe impl Sharable for Box<[T]> { type ReadGuard<'g> = Box<[T::ReadGuard<'g>]> where Self: 'g; - fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { - for lock in self { - lock.get_ptrs(ptrs); - } - } - - unsafe fn guard(&self) -> Self::Guard<'_> { - self.iter().map(|lock| lock.guard()).collect() - } - unsafe fn read_guard(&self) -> Self::ReadGuard<'_> { self.iter().map(|lock| lock.read_guard()).collect() } } -// I'd make a generic impl> Lockable for I -// but I think that'd require sealing up this trait - -impl LockableAsMut for [T; N] { - type Inner<'a> - = [T::Inner<'a>; N] +unsafe impl Sharable for Vec { + type ReadGuard<'g> + = Box<[T::ReadGuard<'g>]> where - Self: 'a; - - fn as_mut(&mut self) -> Self::Inner<'_> { - unsafe { - let mut guards = MaybeUninit::<[MaybeUninit>; N]>::uninit().assume_init(); - for (i, lock) in self.iter_mut().enumerate() { - guards[i].write(lock.as_mut()); - } + Self: 'g; - guards.map(|g| g.assume_init()) - } + unsafe fn read_guard(&self) -> Self::ReadGuard<'_> { + self.iter().map(|lock| lock.read_guard()).collect() } } -impl LockableIntoInner for [T; N] { - type Inner = [T::Inner; N]; +unsafe impl OwnedLockable for Box<[T]> {} - fn into_inner(self) -> Self::Inner { - unsafe { - let mut guards = MaybeUninit::<[MaybeUninit; N]>::uninit().assume_init(); - for (i, lock) in self.into_iter().enumerate() { - guards[i].write(lock.into_inner()); - } +unsafe impl Lockable for Vec { + // There's no reason why I'd ever want to extend a list of lock guards + type Guard<'g> + = Box<[T::Guard<'g>]> + where + Self: 'g; - guards.map(|g| g.assume_init()) + fn get_ptrs<'a>(&'a self, ptrs: &mut Vec<&'a dyn RawLock>) { + for lock in self { + lock.get_ptrs(ptrs); } } -} - -impl LockableAsMut for Box<[T]> { - type Inner<'a> - = Box<[T::Inner<'a>]> - where - Self: 'a; - fn as_mut(&mut self) -> Self::Inner<'_> { - self.iter_mut().map(LockableAsMut::as_mut).collect() + unsafe fn guard(&self) -> Self::Guard<'_> { + self.iter().map(|lock| lock.guard()).collect() } } +// I'd make a generic impl> Lockable for I +// but I think that'd require sealing up this trait + // TODO: using edition 2024, impl LockableIntoInner for Box<[T]> impl LockableAsMut for Vec { @@ -543,12 +490,6 @@ impl LockableIntoInner for Vec { } } -unsafe impl Sharable for [T; N] {} -unsafe impl Sharable for Box<[T]> {} -unsafe impl Sharable for Vec {} - -unsafe impl OwnedLockable for [T; N] {} -unsafe impl OwnedLockable for Box<[T]> {} unsafe impl OwnedLockable for Vec {} #[cfg(test)] @@ -567,28 +508,6 @@ mod tests { assert!(std::ptr::addr_eq(lock_ptrs[0], mutref)); } - #[test] - fn read_lock_get_ptrs() { - let rwlock = RwLock::new(5); - let readlock = ReadLock::new(&rwlock); - let mut lock_ptrs = Vec::new(); - readlock.get_ptrs(&mut lock_ptrs); - - assert_eq!(lock_ptrs.len(), 1); - assert!(std::ptr::addr_eq(lock_ptrs[0], &rwlock)); - } - - #[test] - fn write_lock_get_ptrs() { - let rwlock = RwLock::new(5); - let writelock = WriteLock::new(&rwlock); - let mut lock_ptrs = Vec::new(); - writelock.get_ptrs(&mut lock_ptrs); - - assert_eq!(lock_ptrs.len(), 1); - assert!(std::ptr::addr_eq(lock_ptrs[0], &rwlock)); - } - #[test] fn array_get_ptrs_empty() { let locks: [Mutex<()>; 0] = []; -- cgit v1.2.3